diff --git a/.travis.yml b/.travis.yml index 44bd258..87df327 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,10 @@ services: - mysql before_script: - mysql -e 'create database trapp;' + - sh -c "mysql < tests/fixtures/tbl_gameminutes.sql" - sh -c "mysql < tests/fixtures/tbl_games.sql" - sh -c "mysql < tests/fixtures/tbl_players.sql" - sh -c "mysql < tests/fixtures/tbl_teams.sql" + - sh -c "mysql < tests/fixtures/test_data.sql" script: - tox -e $TOX_ENV diff --git a/tests/fixtures/tbl_gameminutes.sql b/tests/fixtures/tbl_gameminutes.sql new file mode 100644 index 0000000..5e286e5 --- /dev/null +++ b/tests/fixtures/tbl_gameminutes.sql @@ -0,0 +1,37 @@ +/* +SQLyog Community v10.3 +MySQL - 5.5.28-log : Database - trapp +********************************************************************* +*/ +USE trapp; + +/*!40101 SET NAMES utf8 */; + +/*!40101 SET SQL_MODE=''*/; + +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; +/*Table structure for table `tbl_gameminutes` */ + +DROP TABLE IF EXISTS `tbl_gameminutes`; + +CREATE TABLE `tbl_gameminutes` ( + `ID` double NOT NULL AUTO_INCREMENT, + `GameID` int(11) DEFAULT NULL, + `TeamID` double DEFAULT NULL, + `PlayerID` double DEFAULT NULL, + `TimeOn` tinyint(3) unsigned DEFAULT '0', + `TimeOff` tinyint(3) unsigned DEFAULT '0', + `Ejected` int(11) DEFAULT '0', + PRIMARY KEY (`ID`), + UNIQUE KEY `ID` (`ID`), + KEY `ID_2` (`ID`), + KEY `GameID` (`GameID`) +) ENGINE=MyISAM AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; diff --git a/tests/fixtures/test_data.sql b/tests/fixtures/test_data.sql new file mode 100644 index 0000000..382bd34 --- /dev/null +++ b/tests/fixtures/test_data.sql @@ -0,0 +1,47 @@ +/* +SQLyog Community v10.3 +MySQL - 5.5.28-log : Database - trapp +********************************************************************* +*/ + + +/*!40101 SET NAMES utf8 */; + +/*!40101 SET SQL_MODE=''*/; + +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; +USE `trapp`; + +/*Data for the table `tbl_gameminutes` */ + +insert into `tbl_gameminutes`(`ID`,`GameID`,`TeamID`,`PlayerID`,`TimeOn`,`TimeOff`,`Ejected`) values (1,1,2,3,0,90,0); + +/*Data for the table `tbl_players` */ + +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (1,'the Rabbit','Harvey','Goalkeeper',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Bedford Falls, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (2,'Rains','Claude','Defender',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Bedford Falls, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (3,'Man','Invisible','Defender',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Bedford Falls, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (4,'Phantom','','Midfielder',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Bedford Falls, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (5,'Bogeyman','','Defender',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Bedford Falls, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (6,'Gyfre','','Defender',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Bedford Falls, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (7,'Potter','Harry','Midfielder',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Surrey, England',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (8,'Storm','Sue','Midfielder',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Bedford Falls, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (9,'Griffin','','Defender',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Bedford Falls, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (10,'Vehl','Mahr','Midfielder',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Bedford Falls, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (11,'Faustus','Doctor','Midfielder',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Bedford Falls, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (12,'Mephistopheles','','Midfielder',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Bedford Falls, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (13,'Caine','Sebastian','Forward',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Bedford Falls, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (14,'Hart','Amos','Forward',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Bedford Falls, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (15,'Player','Sample','Midfielder',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Oneonta, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (16,'Substitution','','Midfielder',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Oneonta, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (17,'Substitution','First','Midfielder',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Oneonta, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (18,'Substitution','Second','Midfielder',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Oneonta, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); +insert into `tbl_players`(`ID`,`LastName`,`FirstName`,`Position`,`RosterNumber`,`Picture`,`Class`,`Eligible`,`College`,`Current_Club`,`ContractStatus`,`LastClub`,`YouthClub`,`Height_Feet`,`Height_Inches`,`Birthplace`,`HomeTown`,`Citizenship`,`Bio`,`Visible`,`Award_Pts`,`Intl_Pts`,`Weight`,`DOB`,`Expansion2014`) values (19,'Substitution','Third','Midfielder',NULL,'coming_soon.gif',0,'0','','',NULL,NULL,NULL,NULL,NULL,NULL,'Oneonta, NY',NULL,NULL,0,NULL,NULL,NULL,'1980-01-01',0); + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; diff --git a/tests/test_gameminute.py b/tests/test_gameminute.py new file mode 100644 index 0000000..d159640 --- /dev/null +++ b/tests/test_gameminute.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import +import pytest +from trapp.gameminute import GameMinute +from trapp.log import Log + + +def test_gameminute_init(): + gm = GameMinute() + # object types + assert isinstance(gm, GameMinute) + assert isinstance(gm.data, dict) + # Default values + + +def test_gameminute_connect(): + gm = GameMinute() + assert hasattr(gm, 'db') is False + gm.connectDB() + assert hasattr(gm, 'db') + + +def test_gameminute_disconnect(): + gm = GameMinute() + gm.connectDB() + assert hasattr(gm, 'db') + gm.disconnectDB() + assert hasattr(gm, 'db') is False + + +def test_gameminute_checkData(): + gm = GameMinute() + required = ['GameID', 'TeamID', 'PlayerID'] + + # This should raise a format error + with pytest.raises(RuntimeError) as excinfo: + needle = 'Foo' + gm.checkData(needle, required) + assert 'lookupID requires a dictionary' in str(excinfo.value) + + # This should raise a field error + with pytest.raises(RuntimeError) as excinfo: + needle = { + 'Foo': 'Bar' + } + gm.checkData(needle, required) + assert 'Submitted data is missing the following fields' in str(excinfo.value) + + +def test_gameminute_lookupID(): + log = Log('test.log') + gm = GameMinute() + gm.connectDB() + + needle = { + 'GameID': 1, + 'TeamID': 2, + 'PlayerID': 3 + } + result = gm.lookupID(needle, log) + assert len(result) == 1 + + needle = { + 'GameID': 0, + 'TeamID': 0, + 'PlayerID': 0 + } + result = gm.lookupID(needle, log) + assert len(result) == 0 + + +def test_gameminute_saveDict(): + log = Log('test.log') + gm = GameMinute() + gm.connectDB() + + # Formats + with pytest.raises(RuntimeError) as excinfo: + data = 'foo' + gm.saveDict(data, log) + assert 'saveDict requires a dictionary' in str(excinfo.value) + + # Inserts + data = { + 'GameID': 1, + 'TeamID': 1, + 'PlayerID': 1, + 'TimeOn': 0, + 'TimeOff': 90, + 'Ejected': 0 + } + assert gm.saveDict(data, log) is True + assert gm.db.warnings() is None + + # Updates + data = { + 'ID': 1, + 'GameID': 2, + 'TeamID': 2, + 'PlayerID': 2, + 'TimeOn': 0, + 'TimeOff': 89, + 'Ejected': 1 + } + assert gm.saveDict(data, log) is True + assert gm.db.warnings() is None diff --git a/tests/test_importer.py b/tests/test_importer.py index 0478a7b..c3e74cd 100644 --- a/tests/test_importer.py +++ b/tests/test_importer.py @@ -102,7 +102,7 @@ def test_importer_parsePlayer(excel, lineup): player = 'Sample Player' result = importer.parsePlayer(player, game, team) assert len(result) == 1 - assert result == [{'playername': 'Sample Player', 'timeon': 0, 'timeoff': 90, 'ejected': False, 'matchid': 1, 'teamid': 1}] + assert result == [{'PlayerID': 15, 'PlayerName': 'Sample Player', 'TimeOn': 0, 'TimeOff': 90, 'Ejected': False, 'GameID': 1, 'TeamID': 1}] player = "Sample Player (Substitution 50')" result = importer.parsePlayer(player, game, team) assert len(result) == 2 @@ -115,10 +115,10 @@ def test_importer_parsePlayer(excel, lineup): player = 'Sample Player (First Substitution 50 (Second Substitution 76 (Third Substitution 84 (sent off 88))))' result = importer.parsePlayer(player, game, team) assert len(result) == 4 - assert result[3]['playername'] == 'Third Substitution' - assert result[3]['ejected'] is True - assert result[3]['timeon'] == 84 - assert result[3]['timeoff'] == 88 + assert result[3]['PlayerName'] == 'Third Substitution' + assert result[3]['Ejected'] is True + assert result[3]['TimeOn'] == 84 + assert result[3]['TimeOff'] == 88 # starter = 'Sample Player' # gameID = 1 @@ -137,6 +137,17 @@ def test_importer_parsePlayerRemoveTime(excel, lineup): assert player == 'Sample Player' +def test_importer_parsePlayerSplit(excel): + log = Log('test.log') + importer = ImporterLineups(excel, log) + string = 'Player Name' + assert importer.parsePlayerSplit(string) == ['Player Name'] + string = 'Player Name (Substitute 63)' + assert importer.parsePlayerSplit(string) == ['Player Name', 'Substitute 63'] + string = 'Sample Player (First Substitution 50 (Second Substitution 76 (Third Substitution 84 (sent off 88))))' + assert importer.parsePlayerSplit(string) == ['Sample Player', 'First Substitution 50', 'Second Substitution 76', 'Third Substitution 84', 'sent off 88'] + + def test_importer_setLog(excel): log = Log('test.log') log2 = Log('test2.log') diff --git a/tests/test_player.py b/tests/test_player.py index 886d6ec..b103ab7 100644 --- a/tests/test_player.py +++ b/tests/test_player.py @@ -76,6 +76,37 @@ def test_player_lookupID(): # Need a test of successful lookups +def test_player_lookupIDbyName(): + # Setup + log = Log('test.log') + p = Player() + p.connectDB() + + # Format error + with pytest.raises(RuntimeError) as excinfo: + needle = 'Wil' + p.lookupIDbyName(needle, log) + assert 'lookupIDbyName requires a dictionary' in str(excinfo.value) + + # Missing fields error + with pytest.raises(RuntimeError) as excinfo: + needle = { + 'FirstName': 'Wil', + 'LastName': 'Trapp' + } + p.lookupIDbyName(needle, log) + assert 'Submitted data is missing the following fields' in str(excinfo.value) + + needle = { + 'PlayerName': 'asdf', + } + assert p.lookupIDbyName(needle, log) == [] + + needle = { + 'PlayerName': 'Delete Me', + } + assert len(p.lookupIDbyName(needle, log)) > 1 + def test_player_merge(): p = Player() assert p.merge(1, 2) is False diff --git a/trapp/gameminute.py b/trapp/gameminute.py new file mode 100644 index 0000000..ca03b98 --- /dev/null +++ b/trapp/gameminute.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import +from trapp.database import Database + + +class GameMinute(): + + def __init__(self): + self.data = {} + + def connectDB(self): + self.db = Database() + self.db.connect() + + def disconnectDB(self): + self.db.disconnect() + del self.db + + def checkData(self, data, required): + # This checks a submitted data dictionary for required fields. + # 1) data must be a dictionary + if not (isinstance(data, dict)): + raise RuntimeError('lookupID requires a dictionary') + + # 2) data must have certain fields + missing = [] + for term in required: + if term not in data: + missing.append(term) + if (len(missing) > 0): + raise RuntimeError( + 'Submitted data is missing the following fields: ' + + str(missing) + ) + + def lookupID(self, data, log): + # Do we have a record of player X appearing in game Y for team Z? + + # Check submitted data for format and fields + required = ['GameID', 'TeamID', 'PlayerID'] + self.checkData(data, required) + + sql = ('SELECT ID ' + 'FROM tbl_gameminutes ' + 'WHERE GameID = %s ' + ' AND TeamID = %s ' + ' AND PlayerID = %s') + rs = self.db.query(sql, ( + data['GameID'], + data['TeamID'], + data['PlayerID'] + )) + if (rs.with_rows): + records = rs.fetchall() + appearances = [] + for item in records: + appearances.append(item[0]) + + return appearances + + def saveDict(self, data, log): + # Verify that data is a dictionary + if not (isinstance(data, dict)): + raise RuntimeError('saveDict requires a dictionary') + + if ('ID' in data): + # Update + log.message('Record ID provided - we update') + sql = ('UPDATE tbl_gameminutes SET ' + 'GameID = %s, ' + 'TeamID = %s, ' + 'PlayerID = %s, ' + 'TimeOn = %s, ' + 'TimeOff = %s, ' + 'Ejected = %s ' + 'WHERE ID = %s') + rs = self.db.query(sql, ( + data['GameID'], + data['TeamID'], + data['PlayerID'], + data['TimeOn'], + data['TimeOff'], + data['Ejected'], + data['ID'] + )) + else: + log.message('No Record ID provided - we insert') + sql = ('INSERT INTO tbl_gameminutes ' + '(GameID, TeamID, PlayerID, TimeOn, TimeOff, Ejected)' + 'VALUES ' + '(%s, %s, %s, %s, %s, %s)') + rs = self.db.query(sql, ( + data['GameID'], + data['TeamID'], + data['PlayerID'], + data['TimeOn'], + data['TimeOff'], + data['Ejected'] + )) + log.message(str(rs)) + + return True diff --git a/trapp/importer.py b/trapp/importer.py index 698573a..b6d8f93 100644 --- a/trapp/importer.py +++ b/trapp/importer.py @@ -2,6 +2,7 @@ from __future__ import absolute_import from trapp.spreadsheet import Spreadsheet from trapp.game import Game +from trapp.gameminute import GameMinute from trapp.player import Player from trapp.team import Team @@ -174,14 +175,14 @@ def adjustTimeOff(self, result, lastOff): # Need to track backwards through list, transferring timeoff for x in reversed(result): - x['timeoff'] = lastOff + x['TimeOff'] = lastOff if (sentOff is True): - x['ejected'] = True + x['Ejected'] = True sentOff = False - if (x['playername'] == 'sent off' or x['playername'] == 'ejected'): + if (x['PlayerName'] == 'sent off' or x['PlayerName'] == 'ejected'): result.remove(x) sentOff = True - lastOff = x['timeon'] + lastOff = x['TimeOn'] return result @@ -190,6 +191,26 @@ def correctValues(self): record['Date'] = self.source.recoverDate(record['Date']) return True + def importPlayer(self, player): + self.log.message(str(player)) + gm = GameMinute() + gm.connectDB() + appearanceID = gm.lookupID(player, self.log) + if (len(appearanceID) > 1): + # We have more than one record of this player/team/game. + # This is a problem. + self.errored += 1 + elif (len(appearanceID) == 1): + # We already have a record of this player/team/game. + # We add that appearanceID, to ensure an update operation. + player['ID'] = appearanceID[0] + gm.saveDict(player, self.log) + self.imported += 1 + else: + gm.saveDict(player, self.log) + self.imported += 1 + return True + def importRecord(self, record): self.log.message('Importing lineup ' + str(record)) # Need to identify gameID @@ -217,23 +238,21 @@ def importRecord(self, record): game = g.lookupID(needle, self.log) self.log.message('Found games: ' + str(game)) - if (len(game) > 1): - self.log.message('Multiple games found') - self.skipped += 1 - return False - # If that's the case, then we need to abort processing this game - elif (len(game) == 0): - self.log.message('No matching games found') + + if (len(game) != 1): + self.log.message('Found wrong number of games: ' + str(len(game))) self.skipped += 1 + # If we didn't find one gameID, then we abort processing this game return False - # If that's the case, then we need to abort processing this game - # If we make it to this point, then procesing can continue + # Need to convert gameID from a list of 1 number to an integer + game = game[0] # Parse lineup string self.parseLineup(record['Lineup'], game, teamID) # At this point we have self.players - but need to store them + [self.importPlayer(player) for player in self.players] return True @@ -262,12 +281,16 @@ def parseLineup(self, lineup, game, teamID): self.log.message('ERROR: Wrong number of starters') self.errored += 1 + # self.starters has strings for every starter, combined with any + # substitutes or sendings off. These need to be split into separate + # records for every player, which is done in parsePlayer self.players = [] for starter in self.starters: batch = self.parsePlayer(starter, game, teamID) for item in batch: self.players.append(item) self.log.message(str(self.players)) + # This method returns nothing, as its work is recorded in # self.starters and self.players. @@ -295,41 +318,66 @@ def parsePlayer(self, starter, gameID, teamID): result = [] timeoff = 90 - while (starter.find('(') > 0): - # calculate boundaries - begin = starter.find('(') - end = starter.rfind(')') - # split into outer and starter - outer = starter[:begin - 1] - starter = starter[begin + 1:end] - # split time from outer - timeon = self.parsePlayerTimeOn(outer) - outer = self.parsePlayerRemoveTime(outer) - # store outer - result.append({ - 'playername': outer.strip(), - 'timeon': timeon, - 'timeoff': timeoff, - 'ejected': False, - 'matchid': gameID, - 'teamid': teamID - }) - - # parse last value - timeon = self.parsePlayerTimeOn(starter) - starter = self.parsePlayerRemoveTime(starter) - # store last value - result.append({ - 'playername': starter.strip(), - 'timeon': timeon, - 'timeoff': timeoff, - 'ejected': False, - 'matchid': gameID, - 'teamid': teamID - }) + # Split the player string into a list + result = self.parsePlayerSplit(starter) + + augmented = [] + # parse each member of the list + for string in result: + # Split time from player name + timeon = self.parsePlayerTimeOn(string) + player = self.parsePlayerRemoveTime(string).strip() + + # Look up playerID + playerID = [0] + if (player != 'sent off' and player != 'ejected'): + p = Player() + p.connectDB() + needle = { + 'PlayerName': player, + } + playerID = p.lookupIDbyName(needle, self.log) + + if (len(playerID) == 1): + playerID = playerID[0] + augmented.append({ + 'PlayerID': playerID, + 'PlayerName': player, + 'TimeOn': timeon, + 'TimeOff': timeoff, + 'Ejected': False, + 'GameID': gameID, + 'TeamID': teamID + }) + else: + self.skipped += 1 + self.log.message('_' + str(player) + '_ returned ' + + str(len(playerID)) + ' matches') # Transfer timeon values to previous player's timeoff - result = self.adjustTimeOff(result, timeoff) + result = self.adjustTimeOff(augmented, timeoff) + + return result + + def parsePlayerSplit(self, inputString): + # This takes in a string and splits it into a list. For example: + # "Player Name" -> ["Player Name"] + # "Player Name (Substitute 63)" -> ["Player Name", "Substitute 65"] + # ... and so fort + result = [] + + while (inputString.find('(') > 0): + # Find boundaries to split + begin = inputString.find('(') + end = inputString.rfind(')') + # Perform the split + outer = inputString[:begin - 1] + inputString = inputString[begin + 1:end] + # Append results + result.append(outer) + + # Do stuff + result.append(inputString) return result diff --git a/trapp/player.py b/trapp/player.py index c70dc20..88f2f3a 100644 --- a/trapp/player.py +++ b/trapp/player.py @@ -111,6 +111,38 @@ def lookupID(self, data, log): # How many games matched this data? return players + def lookupIDbyName(self, data, log): + # This takes in a player's name and looks up player ID based on that + # alone + if not (isinstance(data, dict)): + raise RuntimeError('lookupIDbyName requires a dictionary') + + missing = [] + required = ['PlayerName', ] + for term in required: + if term not in data: + missing.append(term) + if (len(missing) > 0): + raise RuntimeError( + 'Submitted data is missing the following fields: ' + + str(missing) + ) + + # See if any game matches these three terms + sql = ('SELECT ID ' + 'FROM tbl_players ' + 'WHERE TRIM(CONCAT(FirstName," ",LastName)) = %s') + rs = self.db.query(sql, ( + data['PlayerName'], + )) + if (rs.with_rows): + records = rs.fetchall() + players = [] + for player in records: + players.append(player[0]) + + return players + def merge(self, fromID, intoID): # This merges one player record into another. # It includes all related tables.