player.py 5.96 KB
Newer Older
1 2 3 4 5 6 7 8
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@author: Antoine Lima, Leo Reynaert, Domitille Jehenne
"""

import logging
from enum import Enum
Antoine Lima's avatar
Antoine Lima committed
9
from http import HTTPStatus
Antoine Lima's avatar
Antoine Lima committed
10

11 12 13
from PyQt5.QtCore import Qt, QCoreApplication, QObject, pyqtSlot, QEvent
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QDialog, QApplication
Antoine Lima's avatar
Antoine Lima committed
14

15
from Babyfut.babyfut import getMainWin, IMG_PATH
Antoine Lima's avatar
Antoine Lima committed
16
from Babyfut.core.ginger import Ginger
17 18
from Babyfut.core.database import Database, DatabaseError
from Babyfut.ui.consent_dialog_ui import Ui_Dialog as ConsentDialogUI
19 20

class Side(Enum):
21 22 23 24 25 26
	'''
	Values of the enum are used throughout the code for indexing purposes, not to be changed
	'''
	Undef = -1
	Left  = 0
	Right = 1
27

Antoine Lima's avatar
Antoine Lima committed
28 29
	@property
	def opposite(self):
30
		return Side.Right if self==Side.Left else Side.Left
31

Antoine Lima's avatar
Antoine Lima committed
32 33 34 35 36
class ConsentDialog(QDialog):
	def __init__(self, parent):
		QDialog.__init__(self, parent)
		self.ui = ConsentDialogUI()
		self.ui.setupUi(self)
37

Antoine Lima's avatar
Antoine Lima committed
38 39 40 41 42 43 44
		self.ui.txtConsent.setHtml(QCoreApplication.translate('consent', '''<p>
			You are about to connect yourself for the first time. We will need to access:
			<ul>
				<li>Your Name and Surname</li>
				<li>Your Picture (if public)</li>
				<li>...</li>
			</ul>
45
			</p>
46

Antoine Lima's avatar
Antoine Lima committed
47 48 49 50 51
			<p>
			It is possible to play withtout connecting yourslef, but this will allow you to keep track of your score and to provide a better experience for you and the ones you play with!
			<br/><br/>
			Do you agree with this?
			</p>'''))
52

Antoine Lima's avatar
Antoine Lima committed
53 54 55 56 57
	def keyPressEvent(self, e):
		if e.key()==Qt.Key_Return:
			self.accept()
		else:
			self.reject()
58

59
class Player(QObject):
Antoine Lima's avatar
Antoine Lima committed
60 61 62 63 64
	__query_infos = 'SELECT id, fname, lname, pic FROM Players WHERE rfid==?'
	__query_time_goals_games = 'SELECT SUM(Matchs.duration) AS timePlayed, SUM(Teams.nGoals) AS goalsScored, COUNT(*) AS gamesPlayed FROM Teams INNER JOIN  Matchs ON (Teams.id==Matchs.winningTeam OR Teams.id==Matchs.losingTeam) WHERE (Teams.player1==? OR player2==?)'
	__query_victories = 'SELECT COUNT(*) AS victories FROM Players INNER JOIN Teams ON (Players.id==Teams.player1 OR Players.id==Teams.player2) INNER JOIN  Matchs ON (Teams.id==Matchs.winningTeam) WHERE Players.id==?'

	_placeholder_pic_path = ':ui/img/placeholder_head.jpg'
65

Antoine Lima's avatar
Antoine Lima committed
66
	def __init__(self, id, rfid, fname, lname, pic_path, stats=None):
Antoine Lima's avatar
Antoine Lima committed
67
		QObject.__init__(self)
68
		self.id = id
69
		self.rfid = rfid
70 71
		self.fname = fname
		self.lname = lname
Antoine Lima's avatar
Antoine Lima committed
72
		self.pic_path = pic_path if pic_path else Player._placeholder_pic_path # Default pic if None
Antoine Lima's avatar
Antoine Lima committed
73 74 75 76 77

		if stats==None:
			self.stats = { 'time_played': 0, 'goals_scored': 0, 'games_played': 0, 'victories': 0 }
		else:
			self.stats = stats
78

79
	@staticmethod
Antoine Lima's avatar
Antoine Lima committed
80
	def fromRFID(rfid):
Antoine Lima's avatar
Antoine Lima committed
81 82
		if Database.instance().rfid_exists(rfid):
			player = Player._loadFromDB(rfid)
Antoine Lima's avatar
Antoine Lima committed
83 84 85 86 87
		else:
			### Retrieve player from API
			# Ask for consent
			consentDialog = ConsentDialog(getMainWin())
			consentDialog.exec()
88

Antoine Lima's avatar
Antoine Lima committed
89
			if consentDialog.result()==QDialog.Accepted:
Antoine Lima's avatar
Antoine Lima committed
90
				player = Player._loadFromAPI(rfid)
Antoine Lima's avatar
Antoine Lima committed
91
			else:
Antoine Lima's avatar
Antoine Lima committed
92 93 94 95
				logging.debug('Consent refused when retrieving a player, returning Guest')
				player = PlayerGuest

		return player
96

Antoine Lima's avatar
Antoine Lima committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
	@staticmethod
	def _loadFromDB(rfid):
		db = Database.instance()
		try:
			# Retrieve generic informations
			id, fname, lname, pic = db.select_one(Player.__query_infos, rfid)

			# Retrieve stats
			stats = {}
			stats['time_played'], stats['goals_scored'], stats['games_played'] = db.select_one(Player.__query_time_goals_games, id, id)
			stats['victories'], = db.select_one(Player.__query_victories, id)

			for key, val in stats.items():
				if val==None:
					stats[key] = 0

			return Player(id, rfid, fname, lname, pic, stats)

		except DatabaseError as e:
			logging.warn('DB Error: {}'.format(e))
			return PlayerGuest

	@staticmethod
	def _loadFromAPI(rfid):
		'''
		Retrieves a player's informations from the Ginger API
		'''
Antoine Lima's avatar
Antoine Lima committed
124
		response = Ginger.instance.get('badge/{}'.format(rfid))
Antoine Lima's avatar
Antoine Lima committed
125 126 127 128 129 130 131 132 133
		if isinstance(response, HTTPStatus):
			logging.debug('Request to Ginger failed ({}): returning Guest'.format(response.value))
			return PlayerGuest
		else:
			infos = json.loads(response)
			Database.instance().insert_player(infos['rfid'], infos['nom'], infos['prenom'])

		return Player._loadFromDB(rfid)

Antoine Lima's avatar
Antoine Lima committed
134
	def displayImg(self, container_widget):
135 136 137 138
		self.pic_container = container_widget

		if self.pic_path.startswith('http'):
			# Download from the internet
139 140 141
			self.pic_container.setStyleSheet('border-image: url({});'.format(Player._placeholder_pic_path))
			Downloader.instance().request(self.pic_path, os.path.join(IMG_PATH, '{}.jpg'.format(self.id)))
			Downloader.instance().finished.connect(self._downloader_callback)
142 143
		else:
			# Already downloaded and stored locally
144 145 146 147 148 149 150 151 152
			self.pic_container.setStyleSheet('border-image: url({});'.format(self.pic_path))
			self._forceWidgetUpdate()
			QApplication.processEvents()

	@pyqtSlot(str)
	def _downloader_callback(self, path):
		self.pic_path = path
		Downloader.instance().finished.disconnect(self._downloader_callback)
		self.displayImg(self.pic_container)
153

Antoine Lima's avatar
Antoine Lima committed
154
	def forgetPicture(self):
Antoine Lima's avatar
Antoine Lima committed
155 156 157
		self.pic_path = Player._placeholder_pic_path
		Database.instance().delete_playerpic(self.id)

Antoine Lima's avatar
Antoine Lima committed
158
	def make_private(self):
Antoine Lima's avatar
Antoine Lima committed
159 160 161
		self.private = True
		Database.instance().make_player_private(self.id)

162 163
	@property
	def name(self):
164
		return '{} {}'.format(self.fname, self.lname.upper())
165

166
	@property
Antoine Lima's avatar
Antoine Lima committed
167
	def stats_property(self):
168 169 170 171 172
		'''
		Compatibility property allowing to access stats as a object member and not dict
		ex: player.stats['victories'] can be accessed with player.stats_property.victories'
		This is mostly used for sorting players in leaderboard.py
		'''
Antoine Lima's avatar
Antoine Lima committed
173 174 175 176 177 178
		class Stat:
			def __init__(self, stats):
				self.victories = stats['victories']
				self.time_played = stats['time_played']
				self.goals_scored = stats['goals_scored']
				self.games_played = stats['games_played']
179

Antoine Lima's avatar
Antoine Lima committed
180
		return Stat(self.stats)
181

182
	@staticmethod
Antoine Lima's avatar
Antoine Lima committed
183
	def allStoredPlayers():
184
		return [Player.fromRFID(rfid) for rfid, in Database.instance().select_all_rfid()]
185

186
PlayerGuest = Player.fromRFID(-1)
187
PlayerEmpty = Player(-1, -42, '', '', Player._placeholder_pic_path, {'time_played':'', 'goals_scored':'', 'games_played':'', 'victories': ''})