Skip to content

Commit

Permalink
Merge pull request #4 from thewickest/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
thewickest authored Oct 14, 2024
2 parents 743141b + dc7903f commit 7b0f16f
Show file tree
Hide file tree
Showing 23 changed files with 359 additions and 128 deletions.
13 changes: 13 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# ENV='local' # just for local
# BYTE_SCORES_PATH = '/home/pi/RetroPie/scores' # just for local
LOGS_PATH = '/home/pi/RetroStats/logs/game_stats.log'
TEXT_SCORES_PATH = '/home/pi/RetroStats/scores'
API_URL = '<your_api_host>:3001'
API_USER = 'alex'
API_PASSWORD = 'test'
DATABASE_HOST = '<your_database_host>'
DATABASE_NAME = 'retrostats'
DATABASE_USER = 'retrostats'
DATABASE_PASSWORD = 'retrostats'
DATABASE_ROOT_USER = 'root'
DATABASE_ROOT_PASSWORD = 'retrostats'
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Makefile
__pycache__
.env
.env
.env.dev
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Creating a test database in the Raspberry PI
sudo apt update
sudo apt upgrade

sudo apt install mariadb-server

# answer yes to every query
sudo mysql_secure_installation

sudo mysql -u root -p

CREATE DATABASE retrostats;

CREATE USER 'retrostats'@'localhost' IDENTIFIED BY 'retrostats';
CREATE USER 'retrostats'@'%' IDENTIFIED BY 'retrostats';

GRANT ALL PRIVILEGES ON retrostats.* TO 'retrostats'@'localhost';
GRANT ALL PRIVILEGES ON retrostats.* TO 'retrostats'@'%';

FLUSH PRIVILEGES;

# Enable Mysql ports to be connected outside localhost

sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf

# Change the bind-address line to 0.0.0.0. This will allow to receive external connections
# Restart the service
sudo service mysql restart

CREATE TABLE sessions (id INT AUTO_INCREMENT, initDate DATE, duration INT, score INT, gameId INT, gameName VARCHAR(255), gameEmulator VARCHAR(255), gamePath VARCHAR(255), userId INT, state VARCHAR(255), PRIMARY KEY(id));

# This is and insecure practice meant just for development processes. Instead of 0.0.0.0 you should be allowing the host's IP address
11 changes: 4 additions & 7 deletions api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,17 @@ def login():
"username": API_USER,
"password": API_PASSWORD
}
response = requests.post(f'{API_URL}/auth/login', json=user)
return response.json()
return requests.post(f'{API_URL}/auth/login', json=user)

def createSession(session: str, token: str):
return requests.post(f'{API_URL}/sessions', json=session,
headers={'Authorization': f'Bearer {token}'})


def getGameBySlug(gameName: str, token: str):
response = requests.get(f'{API_URL}/games/slug/{gameName}',
return requests.get(f'{API_URL}/games/slug/{gameName}',
headers={'Authorization': f'Bearer {token}'})
return response.json()

def getPlayerByNfc(nfc: str, token: str):
response = requests.get(f'{API_URL}/players/{nfc}',
headers={'Authorization': f'Bearer {token}'})
return response.json()
return requests.get(f'{API_URL}/players/{nfc}',
headers={'Authorization': f'Bearer {token}'})
8 changes: 7 additions & 1 deletion constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,10 @@
UNDERLINE = '\033[4m'

info = f"[{OKGREEN}INFO{ENDC}]"
error = f"[{FAIL}ERROR{ENDC}]"
error = f"[{FAIL}ERROR{ENDC}]"
warn = f"[{WARNING}WARNING{ENDC}]"

#Session states
PROCESSED = 'PROCESSED'
ERROR = 'ERROR'
CREATED = 'CREATED'
64 changes: 49 additions & 15 deletions dbfuncs.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,62 @@
import mysql.connector
from mysql.connector import Error
from variables import DATABASE_NAME, DATABASE_PASSWORD, DATABASE_USER, DATABASE_HOST
from constants import info

class Datos(object):
class Database(object):
def _init_(self,connection,cursor):
self.connection = connection
self.cursor = cursor

def connect(self):
print("Conectando con la base de datos...")
print(f'{info} Connecting with the backup database...')
try:
self.connection = mysql.connector.connect(host='192.168.3.15',
database='myarcadesystem',
user='every',
password='every')
self.connection = mysql.connector.connect( host = DATABASE_HOST,
database = DATABASE_NAME,
user = DATABASE_USER,
password = DATABASE_PASSWORD )
self.cursor = self.connection.cursor(prepared=True)
except Error as e:
except Exception as e:
print(e)
def insertsesion(self,sesiones):
# TODO: another fetch to the api
print("Insertando sesiones...")
for sesion in sesiones:
query = """INSERT INTO sesion (id_s, id_u, id_j, fechaInicio, tiempo, score)
VALUES (%s,%s,%s,%s,%s,%s)"""
self.cursor.execute(query,sesion)
def insertSessions(self,sessions):
print(f'{info} Creating backup sessions...')
backupSessions = []
for session in sessions:
query = """INSERT INTO sessions (initDate, duration, score, gameId, gameName, gameEmulator, gamePath, userId, state)
VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)"""
statement = (
session['initDate'],
session['duration'],
session['score'],
session['gameId'],
session['gameName'],
session['gameEmulator'],
session['gamePath'],
session['userId'],
'CREATED'
)
self.cursor.execute(query, statement)
backupSessions.append({'id': self.cursor.lastrowid, 'session': session})
return backupSessions

def updateSessions(self, sessions, state):
print(f'{info} Updating backup sessions...')
for session in sessions:
query = 'UPDATE sessions SET state = %s WHERE id = %s'
self.cursor.execute(query, (state, session['id']))

def getErrorSessions(self):
query = 'SELECT * FROM sessions WHERE state = "ERROR"'
self.cursor.execute(query)
columns = [desc[0] for desc in self.cursor.description]
rows = self.cursor.fetchall()
sessions = []
for row in rows:
rowSession = dict(zip(columns, row))
id = rowSession.pop('id')
initDate = rowSession.pop('initDate')
rowSession['initDate'] = str(initDate)
sessions.append({'id': id, 'session': rowSession})
return sessions

def takeid(self,valor):
# TODO: this should be changed by a fetch to the api
Expand Down
13 changes: 12 additions & 1 deletion files.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
SEPARATOR = "|"
import os

# Read the log file
def openLogFile(logPath: str) -> list[str]:
Expand Down Expand Up @@ -34,4 +35,14 @@ def writeLogFile(logPath: str, sessionDate: list[str], sessionTime: str):
# Write in the file
logFile = open(logPath, 'w+')
logFile.writelines(newLines)
logFile.close()
logFile.close()

def deleteFile(path: str):
try:
os.remove(path)
except FileNotFoundError:
print(f"File '{path}' not found.")
except PermissionError:
print(f"Permission denied: Unable to delete '{path}'.")
except Exception as e:
print(f"An error occurred while deleting '{path}': {e}")
98 changes: 77 additions & 21 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,103 @@
from variables import LOGS_PATH
from api.api import createSession, login
from requests import ConnectionError
from constants import info, error
from readScores import copyScoreFile
from constants import info, error, warn, PROCESSED, ERROR, CREATED
from readScores import resetScores
import requests
from dbfuncs import Database

def main():

# Process logs file
rows: list[str] = openLogFile(LOGS_PATH)
print(f'{info} Procesando las horas...')
print(f'{info} Processing time...')
sessionDates: list[str] = getSessionDate(rows)
print(f'{info} Sumando las horas de las sesiones....')
print(f'{info} Adding hours to the sessions....')
sessionTime: str = getSessionTime(sessionDates)
if(len(sessionDates)):
writeLogFile(LOGS_PATH, sessionDates, sessionTime)

rows: list[str] = openLogFile(LOGS_PATH)
print(f'{info} Procesando las sesiones...')
# TODO: add proper type
session: object = getSession(rows)
print(f'{info} Processing the sessions...')

# TODO add error handlers if 401
if(session and session['score'] > 0):
# Login into the API to get the Access token
token = None
try:
response = login()
response.raise_for_status()
data = response.json()
token = data['access_token'] if data else None
except ConnectionError:
print(f'{error} Unable to login into the API. The API is down.')
except requests.exceptions.HTTPError as err:
if(err.response.status_code == 401):
print(f'{error} Unauthorized user!')
except Exception as err:
print(f'{error} Unexpected error!')

# Generate the sessions
# Here we can check for previous sessions with status ERROR and return them
sessions: list[object] = getSession(rows, token)

# Connect to local database
database = None
try:
database = Database()
database.connect()
except:
print('Cant connect to local database')

backupSessions = []
if(database):
# save sesssions in local database
try:
backupSessions = database.insertSessions(sessions)
errorSessions = database.getErrorSessions()
backupSessions = backupSessions + errorSessions
except Exception as err:
print('An error ocurred while inserting the Sessions into the database')

if(len(backupSessions) > 0 and database):

# save session in api
try:
data = login()
token = data["access_token"]
response = createSession(session, token)
response.raise_for_status()

strapiSession = response.json()
# TODO handle errors inside the api sevice
print(f'{info} Session created succesfully')
print(f'{info}', strapiSession)
copyScoreFile(session['gameName'])
registerSession(rows)
for session in backupSessions:
response = createSession(session['session'], token)
response.raise_for_status()
strapiSession = response.json()
print(f'{info} Session created succesfully')
print(f'{info}', strapiSession)

database.updateSessions(backupSessions, PROCESSED)

## TODO instead of copying the file, we can register the score inside
## a database
# copyScoreFile(session['gameName'])
# updateSession(id, 'PROCESSED')
# NOTE each time a player starts a new game, the byte scores are new,
# that means that he/she does not need to have a high score to save the
# score into the system. Just the minimum
except ConnectionError as err:
print(f'{error} There was some error connecting to the API. Probably is down')
database.updateSessions(backupSessions, ERROR)
except requests.exceptions.HTTPError as err:
print(f'{error}', err)
print(f'{error}', response.json())
database.updateSessions(backupSessions, ERROR)
else:
print(f'{info} No session to register')
print(f'{warn} No sessions to register')

# even if the creation of the session goes wrong, we reset the state of the scores to make sure that the next session
# doesn't conflict with the new ones.
# This is because we don't have any way to determine which score belongs to which session if there is a many to many relation.
try:
registerSession(rows)
resetScores(sessions)
except Exception as err:
print(f'{error} There was an error while reseting the scores')
print(f'{error}', err)

database.close()

if __name__ == "__main__":
main()
Loading

0 comments on commit 7b0f16f

Please sign in to comment.