diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 776850c..8089b9e 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -13,7 +13,7 @@ jobs:
app-tests:
runs-on: ubuntu-latest
env:
- OPENAI_API_KEY: "sk-proj-OWUfGkcPjXgwaoVtRDSVT3BlbkFJHNUmsndNjL4JJDWRQjUL"
+ OPENAI_API_KEY: "sk-svcacct-XilnF3KG3SaQiqbSj3MlT3BlbkFJxgpB6yDM9CrjxfxhlpMz"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
diff --git a/.gitignore b/.gitignore
index e1a80df..f495d3a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
# Ignore .env files
.env
-src/utils/globals.py
\ No newline at end of file
+src/utils/globals.py
+__pycache__/
+.pytest_cache
\ No newline at end of file
diff --git a/Home.py b/Home.py
index eb351c1..f5699b5 100644
--- a/Home.py
+++ b/Home.py
@@ -1,6 +1,4 @@
import streamlit as st
-import login as login
-import profile
from pages.Profile import profile_page
from login import login_page
diff --git a/login.py b/login.py
index 7142645..be73d28 100644
--- a/login.py
+++ b/login.py
@@ -23,7 +23,7 @@
""", unsafe_allow_html=True)
# Configuração da Conexão com o BD
-DATABASE_URL = "postgresql://root:wRoNcAkjnwCvGRdxD2OKAeSevhOLwJ5b@dpg-cq6i442ju9rs73e8bleg-a.oregon-postgres.render.com/loginbd_fg6e"
+DATABASE_URL = "postgresql://mygamehub:XnDyt3Xa8O66bmE7Jbc3ly6zZ3f4eiGH@dpg-cqlnivdumphs7397s8k0-a.oregon-postgres.render.com/loginbd_6tj3"
engine = create_engine(DATABASE_URL)
Session = sessionmaker(bind=engine)
session = Session()
@@ -34,8 +34,6 @@
Column('id', Integer, primary_key=True),
Column('username', String, unique=True),
Column('password', String),
- Column('name', String),
- Column('gender', String),
autoload_with=engine)
# Funções de hashing e verificação de senha
diff --git a/pages/Profile.py b/pages/Profile.py
index 581ee59..fc97bb4 100644
--- a/pages/Profile.py
+++ b/pages/Profile.py
@@ -1,36 +1,31 @@
import streamlit as st
import pandas as pd
+import plotly.graph_objects as go
from utils import session, users, profiles
from werkzeug.security import generate_password_hash, check_password_hash
def plot_avg_game_ratings_plotly(reviews, jogos):
if not reviews.empty:
- # Ajusta as notas para a escala de 0 a 5
reviews['nota'] = reviews['nota'] / 2
-
- # Calcula a média de notas por jogo
avg_ratings = reviews.groupby('jogo_id')['nota'].mean()
avg_ratings = avg_ratings.to_frame().join(jogos.set_index('id')['title']).rename(columns={'nota': 'avg_rating', 'title': 'game_title'})
avg_ratings = avg_ratings.sort_values('avg_rating', ascending=False)
- # Criação do gráfico
fig = go.Figure()
-
for index, row in avg_ratings.iterrows():
fig.add_trace(go.Bar(
- x=[row['game_title']],
+ x=[row['game_title']],
y=[row['avg_rating']],
- text=[f"{'★' * int(round(row['avg_rating']))} ({row['avg_rating']:.1f})"], # Mostra estrelas e a nota
+ text=[f"{'★' * int(round(row['avg_rating']))} ({row['avg_rating']:.1f})"],
textposition='auto',
))
- # Personalizações adicionais
fig.update_layout(
title='Média de Notas por Jogo (em estrelas)',
xaxis_title='Jogos',
yaxis_title='Nota Média',
- yaxis=dict(range=[0,5]), # Define o limite do eixo y para 5
- plot_bgcolor='rgba(0,0,0,0)'
+ yaxis=dict(range=[0, 5]),
+ plot_bgcolor='rgba(0, 0, 0, 0)'
)
return fig
@@ -38,11 +33,10 @@ def plot_avg_game_ratings_plotly(reviews, jogos):
return None
def load_data():
- jogos = pd.read_csv("src/data/processed_data.csv") # Ajuste o caminho do arquivo se necessário
+ jogos = pd.read_csv("src/data/processed_data.csv")
jogos['id'] = range(1, len(jogos) + 1)
return jogos
-# Função para carregar avaliações (simulação de dados persistidos)
def load_reviews():
try:
return pd.read_csv('src/data/reviews.csv')
@@ -50,107 +44,118 @@ def load_reviews():
return pd.DataFrame(columns=["jogo_id", "usuario", "nota", "comentario", "favorito", "sentimento"])
def delete_account(user_id):
- # Deletar perfil
session.query(profiles).filter(profiles.c.user_id == user_id).delete()
- # Deletar usuário
session.query(users).filter(users.c.id == user_id).delete()
session.commit()
-def profile_page():
- if 'username' not in st.session_state or not st.session_state.get('logged_in', False):
- st.error('Por favor, faça login primeiro.')
- return
-
- username = st.session_state.username
- st.title('Perfil')
- st.sidebar.markdown("# Profile 👤")
-
+def get_user_profile(username):
user = session.query(users).filter(users.c.username == username).first()
profile = session.query(profiles).filter(profiles.c.user_id == user.id).first()
-
if profile is None:
profile = profiles.insert().values(user_id=user.id)
session.execute(profile)
session.commit()
profile = session.query(profiles).filter(profiles.c.user_id == user.id).first()
+ return user, profile
- st.sidebar.title("Minha Conta")
- settings_mode = st.sidebar.radio("Escolha uma opção:", ["Perfil", "Configurações da Conta"])
+def display_profile(profile, user):
+ edit_mode = st.session_state.get('edit_mode', False)
- if settings_mode == "Perfil":
- edit_mode = st.session_state.get('edit_mode', False)
-
- if edit_mode:
- with st.form("profile_info"):
- full_name = st.text_input("Nome Completo", value=profile.full_name if profile.full_name else "")
- bio = st.text_area("Bio", value=profile.bio if profile.bio else "")
-
- save_changes = st.form_submit_button("Salvar Alterações")
- if save_changes:
- session.query(profiles).filter(profiles.c.user_id == user.id).update({
- 'full_name': full_name,
- 'bio': bio
- })
- session.commit()
- st.session_state.edit_mode = False # Turn off edit mode after saving
- st.success("Perfil atualizado com sucesso!")
-
- st.form_submit_button("Cancelar", on_click=lambda: st.session_state.__setitem__('edit_mode', False))
- else:
- st.write(f"**Nome Completo:** {profile.full_name if profile.full_name else 'Não definido'}")
- st.write(f"**Bio:** {profile.bio if profile.bio else 'Não definida'}")
- st.button("Editar", on_click=lambda: st.session_state.__setitem__('edit_mode', True))
+ if edit_mode:
+ with st.form("profile_info"):
+ full_name = st.text_input("Nome Completo", value=profile.full_name if profile.full_name else "")
+ bio = st.text_area("Bio", value=profile.bio if profile.bio else "")
- st.write("---")
- st.write("**Avaliações**")
+ save_changes = st.form_submit_button("Salvar Alterações")
+ if save_changes:
+ update_profile(user.id, full_name, bio)
+ st.session_state.edit_mode = False
+ st.success("Perfil atualizado com sucesso!")
+
+ st.form_submit_button("Cancelar", on_click=lambda: st.session_state.__setitem__('edit_mode', False))
+ else:
+ st.write(f"**Nome Completo:** {profile.full_name if profile.full_name else 'Não definido'}")
+ st.write(f"**Bio:** {profile.bio if profile.bio else 'Não definida'}")
+ st.button("Editar", on_click=lambda: st.session_state.__setitem__('edit_mode', True))
+
+def update_profile(user_id, full_name, bio):
+ session.query(profiles).filter(profiles.c.user_id == user_id).update({
+ 'full_name': full_name,
+ 'bio': bio
+ })
+ session.commit()
- reviews = load_reviews()
- user_reviews = reviews[reviews['usuario'] == username]
+def display_reviews(username):
+ reviews = load_reviews()
+ user_reviews = reviews[reviews['usuario'] == username]
+
+ if not user_reviews.empty:
+ jogos = load_data()
+ user_reviews = user_reviews.merge(jogos[['id', 'title']], left_on='jogo_id', right_on='id')
+
+ for _, row in user_reviews.iterrows():
+ st.write(f"**Jogo:** {row['title']}")
+ st.write(f"**Nota:** {row['nota']}")
+ st.write(f"**Comentário:** {row['comentario']}")
+ st.write(f"**Favorito:** {'Sim' if row['favorito'] else 'Não'}")
+ st.write("---")
+
+ if st.button("Mostrar Gráfico de Avaliações"):
+ fig = plot_avg_game_ratings_plotly(user_reviews, jogos)
+ if fig:
+ st.plotly_chart(fig)
+ else:
+ st.write("Você ainda não fez nenhuma avaliação.")
- if not user_reviews.empty:
- jogos = load_data()
- user_reviews = user_reviews.merge(jogos[['id', 'title']], left_on='jogo_id', right_on='id')
+def update_password(user_id, new_password):
+ account_data = {'password': generate_password_hash(new_password)}
+ session.query(users).filter(users.c.id == user_id).update(account_data)
+ session.commit()
- for _, row in user_reviews.iterrows():
- st.write(f"**Jogo:** {row['title']}")
- st.write(f"**Nota:** {row['nota']}")
- st.write(f"**Comentário:** {row['comentario']}")
- st.write(f"**Favorito:** {'Sim' if row['favorito'] else 'Não'}")
- st.write("---")
+def account_settings(user):
+ with st.form("account_settings"):
+ new_password = st.text_input("Nova Senha", type="password")
+ confirm_password = st.text_input("Confirmar Nova Senha", type="password")
+
+ save_changes = st.form_submit_button("Salvar Alterações")
+ if save_changes:
+ if new_password and new_password != confirm_password:
+ st.error("As senhas não coincidem.")
+ else:
+ if new_password:
+ update_password(user.id, new_password)
+ st.success("Configurações de conta atualizadas com sucesso!")
+
+ st.write("---")
+ st.write("### Deletar Conta")
+ if st.button("Deletar Conta"):
+ confirm_delete = st.checkbox("Confirmar Deleção de Conta")
+ if confirm_delete:
+ delete_account(user.id)
+ st.session_state.clear()
+ st.success("Conta deletada com sucesso. Por favor, feche o aplicativo.")
- if st.button("Mostrar Gráfico de Avaliações"):
- fig = plot_avg_game_ratings_plotly(user_reviews, jogos)
- if fig:
- st.plotly_chart(fig)
- else:
- st.write("Você ainda não fez nenhuma avaliação.")
+def profile_page():
+ if 'username' not in st.session_state or not st.session_state.get('logged_in', False):
+ st.error('Por favor, faça login primeiro.')
+ return
- else:
- st.header("Configurações de Conta")
+ username = st.session_state.username
+ st.title('Perfil')
+ st.sidebar.markdown("# Profile 👤")
- with st.form("account_settings"):
- new_password = st.text_input("Nova Senha", type="password")
- confirm_password = st.text_input("Confirmar Nova Senha", type="password")
+ user, profile = get_user_profile(username)
- save_changes = st.form_submit_button("Salvar Alterações")
- if save_changes:
- if new_password and new_password != confirm_password:
- st.error("As senhas não coincidem.")
- else:
- if new_password:
- account_data = {'password': generate_password_hash(new_password)}
- session.query(users).filter(users.c.id == user.id).update(account_data)
- session.commit()
- st.success("Configurações de conta atualizadas com sucesso!")
+ st.sidebar.title("Minha Conta")
+ settings_mode = st.sidebar.radio("Escolha uma opção:", ["Perfil", "Configurações da Conta"])
+ if settings_mode == "Perfil":
+ display_profile(profile, user)
st.write("---")
- st.write("### Deletar Conta")
- if st.button("Deletar Conta"):
- confirm_delete = st.checkbox("Confirmar Deleção de Conta")
- if confirm_delete:
- delete_account(user.id)
- st.session_state.clear()
- st.success("Conta deletada com sucesso. Por favor, feche o aplicativo.")
+ st.write("**Avaliações**")
+ display_reviews(username)
+ else:
+ account_settings(user)
if __name__ == "__main__":
profile_page()
\ No newline at end of file
diff --git a/pages/tests/test_busca.py b/pages/tests/test_busca.py
new file mode 100644
index 0000000..54698be
--- /dev/null
+++ b/pages/tests/test_busca.py
@@ -0,0 +1,4 @@
+from streamlit.testing.v1 import AppTest
+def test_busca():
+ at = AppTest.from_file("../🔎 Busca.py").run(timeout=50)
+ assert not at.error, "Erro encontrado durante a execução inicial"
\ No newline at end of file
diff --git a/pages/tests/test_login.py b/pages/tests/test_login.py
new file mode 100644
index 0000000..9f959e5
--- /dev/null
+++ b/pages/tests/test_login.py
@@ -0,0 +1,11 @@
+from streamlit.testing.v1 import AppTest
+
+def test_login():
+ at = AppTest.from_file("../../login.py").run(timeout=50)
+ assert not at.error, "Erro encontrado durante a execução inicial"
+
+ at.text_input[0].input("luishmq").run(timeout=50)
+ at.text_input[1].input("1234").run(timeout=50)
+ at.button[0].click().run(timeout=20)
+
+ assert at.error[0].value == "Nome de usuário ou senha incorretos"
\ No newline at end of file
diff --git a/pages/tests/test_mario.py b/pages/tests/test_mario.py
new file mode 100644
index 0000000..7e62588
--- /dev/null
+++ b/pages/tests/test_mario.py
@@ -0,0 +1,9 @@
+from streamlit.testing.v1 import AppTest
+
+def test_mario():
+ at = AppTest.from_file("../🤖 Mario.py").run(timeout=50)
+ assert not at.error, "Erro encontrado durante a execução inicial"
+
+ at.text_input[0].input("Qual jogo de corrida você recomenda?").run(timeout=50)
+ at.button[0].click().run(timeout=20)
+ assert at.markdown[2].value == "#### Resposta:"
\ No newline at end of file
diff --git a/pages/tests/test_reviews.py b/pages/tests/test_reviews.py
new file mode 100644
index 0000000..752a3c5
--- /dev/null
+++ b/pages/tests/test_reviews.py
@@ -0,0 +1,21 @@
+from streamlit.testing.v1 import AppTest
+import logging
+
+# Configuração de logging
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+def test_reviews():
+ try:
+ at = AppTest.from_file("../🧠 Reviews.py").run(timeout=50)
+ assert not at.error, f"Erro encontrado durante a execução inicial: {at.error}"
+ logger.info("Teste inicial passado sem erros.")
+
+ # Adicione qualquer verificação adicional que possa ser necessária aqui
+
+ except Exception as e:
+ logger.error(f"Exceção durante o teste: {e}")
+ assert False, f"Exceção durante o teste: {e}"
+
+if __name__ == "__main__":
+ test_reviews()
diff --git a/pages/tests/test_wishlist.py b/pages/tests/test_wishlist.py
new file mode 100644
index 0000000..4b84233
--- /dev/null
+++ b/pages/tests/test_wishlist.py
@@ -0,0 +1,21 @@
+from streamlit.testing.v1 import AppTest
+import logging
+
+# Configuração de logging
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+def test_wishlist():
+ try:
+ at = AppTest.from_file("../✨ Wishlist.py").run(timeout=50)
+ assert not at.error, f"Erro encontrado durante a execução inicial: {at.error}"
+ logger.info("Teste inicial passado sem erros.")
+
+# Adicione qualquer verificação adicional que possa ser necessária aqui
+
+ except Exception as e:
+ logger.error(f"Exceção durante o teste: {e}")
+ assert False, f"Exceção durante o teste: {e}"
+
+if __name__ == "__main__":
+ test_reviews()
diff --git "a/pages/\342\234\250 Wishlist.py" "b/pages/\342\234\250 Wishlist.py"
index ae71dd5..7b3493f 100644
--- "a/pages/\342\234\250 Wishlist.py"
+++ "b/pages/\342\234\250 Wishlist.py"
@@ -1,58 +1,93 @@
-import streamlit as st
-from sqlalchemy.orm import sessionmaker
-from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData
-
-# Configuração da Conexão com o BD
-DATABASE_URL = "postgresql://root:wRoNcAkjnwCvGRdxD2OKAeSevhOLwJ5b@dpg-cq6i442ju9rs73e8bleg-a.oregon-postgres.render.com/loginbd_fg6e"
-engine = create_engine(DATABASE_URL)
-metadata = MetaData()
-
-# Tabela users
-users = Table('users', metadata,
- Column('id', Integer, primary_key=True),
- Column('username', String, unique=True),
- Column('password', String))
-
-# Tabela wishlist
-wishlist = Table('wishlist', metadata,
- Column('id', Integer, primary_key=True),
- Column('user_id', Integer),
- Column('game_id', String))
-
-metadata.create_all(engine)
-
-Session = sessionmaker(bind=engine)
-session = Session()
-
-# Verificar se o usuário está logado
-if 'logged_in' not in st.session_state or not st.session_state.logged_in:
- st.warning("Por favor, faça login para acessar a lista de desejos.")
- st.stop()
-
-# Obter informações do usuário logado
-username = st.session_state.username
-user = session.query(users).filter(users.c.username == username).first()
-
-# Página da Lista de Desejos
-st.title('Minha Lista de Desejos')
-st.sidebar.markdown("# Wishlist ✨")
-
-# Adicionar jogo à lista de desejos
-game_id = st.text_input('Nome do Jogo para adicionar à lista de desejos')
-if st.button('Adicionar à lista de desejos'):
- if game_id:
- new_wishlist_item = wishlist.insert().values(user_id=user.id, game_id=game_id)
- session.execute(new_wishlist_item)
- session.commit()
- st.success('Jogo adicionado à lista de desejos com sucesso!')
- else:
- st.error('Por favor, insira o nome de um jogo.')
-
-# Exibir lista de desejos
-st.header('Sua Lista de Desejos')
-wishlist_items = session.query(wishlist).filter(wishlist.c.user_id == user.id).all()
-if wishlist_items:
- for item in wishlist_items:
- st.write(f"Jogo: {item.game_id}")
-else:
- st.write('Sua lista de desejos está vazia.')
+import streamlit as st
+from sqlalchemy.orm import sessionmaker
+from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData
+
+# Configuração da Conexão com o BD
+DATABASE_URL = "postgresql://mygamehub:XnDyt3Xa8O66bmE7Jbc3ly6zZ3f4eiGH@dpg-cqlnivdumphs7397s8k0-a.oregon-postgres.render.com/loginbd_6tj3"
+
+# Configuração da Conexão com o Banco de Dados
+class Database:
+ def __init__(self, url):
+ self.engine = create_engine(url)
+ self.metadata = MetaData()
+ self.metadata.create_all(self.engine)
+ self.Session = sessionmaker(bind=self.engine)
+ self.session = self.Session()
+
+# Facade para Gerenciamento de Usuários e Wishlist
+class WishlistFacade:
+ def __init__(self, db):
+ self.db = db
+ self.users = Table('users', self.db.metadata,
+ Column('id', Integer, primary_key=True),
+ Column('username', String, unique=True),
+ Column('password', String))
+
+ self.wishlist = Table('wishlist', self.db.metadata,
+ Column('id', Integer, primary_key=True),
+ Column('user_id', Integer),
+ Column('game_id', String))
+
+ def get_logged_in_user(self, username):
+ return self.db.session.query(self.users).filter(self.users.c.username == username).first()
+
+ def add_to_wishlist(self, user_id, game_id):
+ new_wishlist_item = self.wishlist.insert().values(user_id=user_id, game_id=game_id)
+ self.db.session.execute(new_wishlist_item)
+ self.db.session.commit()
+
+ def remove_from_wishlist(self, user_id, game_id):
+ self.db.session.execute(self.wishlist.delete().where(self.wishlist.c.user_id == user_id).where(self.wishlist.c.game_id == game_id))
+ self.db.session.commit()
+
+ def get_wishlist_items(self, user_id):
+ return self.db.session.query(self.wishlist).filter(self.wishlist.c.user_id == user_id).all()
+
+# Funções de Interface com o Usuário
+def check_user_logged_in():
+ if 'logged_in' not in st.session_state or not st.session_state.logged_in:
+ st.warning("Por favor, faça login para acessar a lista de desejos.")
+ st.stop()
+
+def display_wishlist(facade, user_id):
+ st.header('Sua Lista de Desejos')
+ wishlist_items = facade.get_wishlist_items(user_id)
+ if wishlist_items:
+ for item in wishlist_items:
+ col1, col2 = st.columns([3, 1])
+ col1.write(f"Jogo: {item.game_id}")
+ if col2.button('Remover', key=item.id):
+ facade.remove_from_wishlist(user_id, item.game_id)
+ st.success(f"Jogo {item.game_id} removido da lista de desejos com sucesso!")
+ st.experimental_rerun() # Atualizar a página para refletir a remoção
+ else:
+ st.write('Sua lista de desejos está vazia.')
+
+# Função Principal
+def main():
+ check_user_logged_in()
+ username = st.session_state.username
+
+ # Instancia a conexão com o banco de dados e a facade
+ db = Database(DATABASE_URL)
+ facade = WishlistFacade(db)
+
+ user = facade.get_logged_in_user(username)
+ if user:
+ st.title('Minha Lista de Desejos')
+ st.sidebar.markdown("# Wishlist ✨")
+
+ game_id = st.text_input('Nome do Jogo para adicionar à lista de desejos')
+ if st.button('Adicionar à lista de desejos'):
+ if game_id:
+ facade.add_to_wishlist(user.id, game_id)
+ st.success('Jogo adicionado à lista de desejos com sucesso!')
+ else:
+ st.error('Por favor, insira o nome de um jogo.')
+
+ display_wishlist(facade, user.id)
+ else:
+ st.error("Não foi possível recuperar o usuário logado.")
+
+if __name__ == "__main__":
+ main()
diff --git "a/pages/\360\237\223\260 Journal.py" "b/pages/\360\237\223\260 Journal.py"
index 01d4931..89ab492 100644
--- "a/pages/\360\237\223\260 Journal.py"
+++ "b/pages/\360\237\223\260 Journal.py"
@@ -129,19 +129,19 @@
unsafe_allow_html=True
)
-# Função para formatar a data de publicação
+
def format_date(date_str):
return datetime.datetime.strptime(date_str, "%Y-%m-%d").strftime("%d %b %Y")
-# Barra de pesquisa
+
search_query = st.text_input("Pesquisar jogo:", "", key="search", placeholder="Digite o nome do jogo...")
-# Obter o ID do artigo da URL
+
query_params = st.experimental_get_query_params()
article_id = query_params.get("article_id", [None])[0]
game_name = query_params.get("game", [None])[0]
-# Filtrar notícias com base na pesquisa
+
if search_query:
filtered_articles = [item for item in news_data if search_query.lower() in item["game"].lower()]
else:
diff --git "a/pages/\360\237\224\216 Busca.py" "b/pages/\360\237\224\216 Busca.py"
index e15a7a4..b2b5df9 100644
--- "a/pages/\360\237\224\216 Busca.py"
+++ "b/pages/\360\237\224\216 Busca.py"
@@ -1,71 +1,116 @@
import pandas as pd
import streamlit as st
-def clean_price(price):
- if price.lower() == 'free':
- return 0.0
- return float(price.replace('$', '').replace(',', ''))
-
-@st.cache_data
-def load_data():
- data = pd.read_csv('src/data/processed_data.csv')
- data['original_price'] = data['original_price'].apply(clean_price) # Limpar preços
- # Limpar as tags para remover chaves e garantir que não haja duplicatas
- data['popular_tags'] = data['popular_tags'].fillna('').apply(lambda x: [tag.strip() for tag in x.strip('[]').replace('"', '').split(',')])
- return data
-
-data = load_data()
-
-# Extrair todas as tags únicas e remover duplicatas
-all_tags = sorted(set(tag for tags in data['popular_tags'] for tag in tags if tag))
-
-page_bg_img = '''
-
-'''
-
-st.markdown(page_bg_img, unsafe_allow_html=True)
-st.sidebar.markdown("# Busca 🔎")
-
-st.title('Sistema de Busca de Jogos')
-query = st.text_input("Digite o nome do jogo que deseja buscar:")
-
-# Slider para filtrar por preço
-min_price, max_price = st.slider(
- 'Selecione o intervalo de preços:',
- min_value=float(data['original_price'].min()),
- max_value=float(data['original_price'].max()),
- value=(float(data['original_price'].min()), float(data['original_price'].max()))
-)
-
-# Campo de seleção para filtrar por tags populares
-selected_tags = st.multiselect('Selecione as tags populares:', all_tags)
-
-if query or selected_tags:
- results = data.copy()
-
- if query:
- results = results[results['title'].str.contains(query, case=False, na=False)]
-
- if selected_tags:
- results = results[results['popular_tags'].apply(lambda tags: all(tag in tags for tag in selected_tags))]
-
- results = results[results['original_price'].between(min_price, max_price)]
-
- st.write(f"Resultados para '{query}' com preço entre {min_price} e {max_price} e tags: {', '.join(selected_tags)}:")
- st.dataframe(results)
-else:
- st.write("Digite o nome de um jogo para buscar e ajuste o filtro de preço e tags se desejar.")
+class GameSearchFacade:
+ def __init__(self):
+ self.data = self.load_and_prepare_data()
+ self.all_tags = self.extract_all_tags()
+
+ @staticmethod
+ def clean_price(price):
+ if price.lower() == 'free':
+ return 0.0
+ return float(price.replace('$', '').replace(',', ''))
+
+ @staticmethod
+ def load_and_prepare_data():
+ data = GameSearchFacade.load_data()
+ data = GameSearchFacade.clean_data(data)
+ return data
+
+ @staticmethod
+ @st.cache_data
+ def load_data():
+ return pd.read_csv('src/data/processed_data.csv')
+
+ @staticmethod
+ def clean_data(data):
+ data['original_price'] = data['original_price'].apply(GameSearchFacade.clean_price)
+ data['popular_tags'] = data['popular_tags'].fillna('').apply(
+ lambda x: [tag.strip() for tag in x.strip('[]').replace('"', '').split(',')]
+ )
+ return data
+
+ def extract_all_tags(self):
+ return sorted(set(tag for tags in self.data['popular_tags'] for tag in tags if tag))
+
+ def search_games(self, query='', selected_tags=None, min_price=None, max_price=None):
+ selected_tags = selected_tags or []
+ results = self.filter_by_query(query)
+ results = self.filter_by_tags(results, selected_tags)
+ results = self.filter_by_price(results, min_price, max_price)
+ return results
+
+ def filter_by_query(self, query):
+ if not query:
+ return self.data.copy()
+ return self.data[self.data['title'].str.contains(query, case=False, na=False)]
+
+ def filter_by_tags(self, data, selected_tags):
+ if not selected_tags:
+ return data
+ return data[data['popular_tags'].apply(lambda tags: all(tag in tags for tag in selected_tags))]
+
+ def filter_by_price(self, data, min_price, max_price):
+ if min_price is None or max_price is None:
+ return data
+ return data[data['original_price'].between(min_price, max_price)]
+
+ def get_all_tags(self):
+ return self.all_tags
+
+# Função para configurar a interface do usuário
+def configure_interface(game_search):
+ set_page_style()
+ st.sidebar.markdown("# Busca 🔎")
+ st.title('Sistema de Busca de Jogos')
+
+ query = st.text_input("Digite o nome do jogo que deseja buscar:")
+
+ min_price, max_price = get_price_range(game_search)
+
+ selected_tags = st.multiselect('Selecione as tags populares:', game_search.get_all_tags())
+
+ results = game_search.search_games(query, selected_tags, min_price, max_price)
+
+ display_results(query, min_price, max_price, selected_tags, results)
+
+def set_page_style():
+ page_bg_img = '''
+
+ '''
+ st.markdown(page_bg_img, unsafe_allow_html=True)
+
+def get_price_range(game_search):
+ return st.slider(
+ 'Selecione o intervalo de preços:',
+ min_value=float(game_search.data['original_price'].min()),
+ max_value=float(game_search.data['original_price'].max()),
+ value=(float(game_search.data['original_price'].min()), float(game_search.data['original_price'].max()))
+ )
+
+def display_results(query, min_price, max_price, selected_tags, results):
+ if not results.empty:
+ st.write(f"Resultados para '{query}' com preço entre {min_price} e {max_price} e tags: {', '.join(selected_tags)}:")
+ st.dataframe(results)
+ else:
+ st.write("Digite o nome de um jogo para buscar e ajuste o filtro de preço e tags se desejar.")
+
+# Instanciar o facade
+game_search = GameSearchFacade()
+
+# Configurar a interface do usuário
+configure_interface(game_search)
\ No newline at end of file
diff --git "a/pages/\360\237\244\226 Mario.py" "b/pages/\360\237\244\226 Mario.py"
index 320c032..a15e581 100644
--- "a/pages/\360\237\244\226 Mario.py"
+++ "b/pages/\360\237\244\226 Mario.py"
@@ -8,68 +8,66 @@
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_core.messages import HumanMessage, AIMessage
from langchain.chains import create_retrieval_chain
-from src.utils.globals import KEY
+
+load_dotenv()
+
+ai_prompt = """
+Você é o Mario, o assistente virtual do MyGameHub.
+
+O seu papel é fornecer assistência informativa e amigável aos usuários com dúvidas sobre jogos e recomendações.
+
+Você deve possuir um conhecimento amplo sobre o mundo dos jogos da Steam para fornecer melhores recomendações e respostas aos usuários.
+
+Pergunta do usuário: {input}
+
+Você deve possuir memória e compreensão de todas as perguntas e respostas anteriores para fornecer respostas coerentes e úteis: {messages}
+
+Responda à pergunta para o usuário de forma agradável, sutil e precisa com base no seguinte contexto: {context}
+"""
@st.cache_resource
def load_data():
+ """Carregando os dados e criando o vectorstore."""
df = pd.read_csv("src/data/processed_data.csv")
texts = df.apply(lambda x: x['title'] + " - " + x['game_description'], axis=1).tolist()
- vectorstore = FAISS.from_texts(texts, embedding=OpenAIEmbeddings(api_key=KEY))
+ vectorstore = FAISS.from_texts(texts, embedding=OpenAIEmbeddings())
return vectorstore.as_retriever()
class Mario:
+ """
+ Classe do assistente virtual Mario do MyGameHub.
+
+ Atributos:
+ model (ChatOpenAI): O modelo de linguagem usado para gerar respostas.
+ chain (object): A cadeia de recuperação de respostas.
+ """
def __init__(self):
- self.prompt_template = self._create_prompt_template()
- self.model = self._initialize_model()
- self.chain = self._create_chain()
- self.retrieval_chain = self._create_retrieval_chain()
+ """Inicializando a instância do Mario, configurando o estado da sessão, o modelo e a cadeia de recuperação."""
self._initialize_session_state()
-
- def _create_prompt_template(self):
- template = """Você é o Mario, o assistente virtual do MyGameHub.
-
- O seu papel é fornecer assistência informativa e amigável aos usuários com dúvidas sobre jogos e recomendações.
-
- Você deve possuir um conhecimento amplo sobre o mundo dos jogos da Steam para fornecer melhores recomendações e respostas aos usuários.
-
- Pergunta do usuário:
-
- {input}
-
- Você deve possuir memória e compreensão de todas as perguntas e respostas anteriores para fornecer respostas coerentes e úteis:
-
- {messages}
-
- Responda à pergunta para o usuário de forma agradável, sutil e precisa com base no seguinte contexto:
-
- {context}
- """
- return ChatPromptTemplate.from_template(template)
-
- def _initialize_model(self):
- return ChatOpenAI(temperature=0.2, model="gpt-4o", api_key=KEY)
-
- def _create_chain(self):
- return (
- self.prompt_template
- | self.model
- | StrOutputParser()
- )
-
- def _create_retrieval_chain(self):
- return create_retrieval_chain(load_data(), self.chain)
+ self.model = ChatOpenAI(temperature=0.2, model="gpt-4o")
+ self.chain = self._create_chain()
def _initialize_session_state(self):
+ """Inicializando o estado da sessão para armazenar o histórico da conversa."""
if 'chat_history' not in st.session_state:
st.session_state['chat_history'] = ChatMessageHistory()
+ def _create_chain(self):
+ """Criando a cadeia de processamento para o agente."""
+ prompt = ChatPromptTemplate.from_template(ai_prompt)
+ chain = prompt | self.model | StrOutputParser()
+ retrieval_chain = create_retrieval_chain(load_data(), chain)
+ return retrieval_chain
+
def get_response(self, user_input):
+ """Obtendo resposta do chatbot."""
st.session_state.chat_history.add_user_message(user_input)
- response = self.retrieval_chain.invoke({"input": user_input, "messages": st.session_state.chat_history.messages})
+ response = self.chain.invoke({"input": user_input, "messages": st.session_state.chat_history.messages})
st.session_state.chat_history.add_ai_message(response["answer"])
return response["answer"]
def display_chat_history(self):
+ """Exibindo o histórico da conversa para o usuário."""
download_str = []
with st.expander("Histórico da Conversa", expanded=True):
for message in reversed(st.session_state.chat_history.messages):
diff --git "a/pages/\360\237\247\240 Reviews.py" "b/pages/\360\237\247\240 Reviews.py"
index f96898d..c50c6c9 100644
--- "a/pages/\360\237\247\240 Reviews.py"
+++ "b/pages/\360\237\247\240 Reviews.py"
@@ -2,109 +2,144 @@
import pandas as pd
import plotly.graph_objects as go
from transformers import pipeline
-
+from sqlalchemy.orm import sessionmaker
+from sqlalchemy import create_engine, Table, Column, Integer, String, Float, MetaData
+
+# Configuração da Conexão com o BD
+DATABASE_URL = "postgresql://mygamehub:XnDyt3Xa8O66bmE7Jbc3ly6zZ3f4eiGH@dpg-cqlnivdumphs7397s8k0-a.oregon-postgres.render.com/loginbd_6tj3"
+engine = create_engine(DATABASE_URL)
+metadata = MetaData()
+
+# Tabela reviews
+reviews_table = Table('reviews', metadata,
+ Column('id', Integer, primary_key=True),
+ Column('jogo_id', Integer),
+ Column('usuario', String),
+ Column('nota', Float),
+ Column('comentario', String),
+ Column('favorito', String),
+ Column('sentimento', String))
+
+metadata.create_all(engine)
+Session = sessionmaker(bind=engine)
+session = Session()
+
+# Carregamento de CSS personalizado
def load_custom_css():
with open('src/data/styleReviews.css') as f:
st.markdown(f'', unsafe_allow_html=True)
-def plot_avg_game_ratings_plotly(reviews, jogos):
- if not reviews.empty:
- # Ajusta as notas para a escala de 0 a 5
- reviews['nota'] = reviews['nota'] / 2
-
- # Calcula a média de notas por jogo
- avg_ratings = reviews.groupby('jogo_id')['nota'].mean()
- avg_ratings = avg_ratings.to_frame().join(jogos.set_index('id')['title']).rename(columns={'nota': 'avg_rating', 'title': 'game_title'})
- avg_ratings = avg_ratings.sort_values('avg_rating', ascending=False)
-
- # Criação do gráfico
- fig = go.Figure()
-
- for index, row in avg_ratings.iterrows():
- fig.add_trace(go.Bar(
- x=[row['game_title']],
- y=[row['avg_rating']],
- text=[f"{'★' * int(round(row['avg_rating']))} ({row['avg_rating']:.1f})"], # Mostra estrelas e a nota
- textposition='auto',
- ))
-
- # Personalizações adicionais
- fig.update_layout(
- title='Média de Notas por Jogo (em estrelas)',
- xaxis_title='Jogos',
- yaxis_title='Nota Média',
- yaxis=dict(range=[0,5]), # Define o limite do eixo y para 5
- plot_bgcolor='rgba(0,0,0,0)'
- )
-
- return fig
- else:
- return None
-
-# Função para carregar os dados dos jogos
-def load_data():
- jogos = pd.read_csv("src/data/processed_data.csv") # Ajuste o caminho do arquivo se necessário
- jogos['id'] = range(1, len(jogos) + 1)
- return jogos
-
-# Função para carregar avaliações (simulação de dados persistidos)
-def load_reviews():
- try:
- return pd.read_csv('src/data/reviews.csv')
- except FileNotFoundError:
- return pd.DataFrame(columns=["jogo_id", "usuario", "nota", "comentario", "favorito"])
-
-# Função para salvar avaliações (persistência de dados)
-def save_reviews(reviews):
- reviews.to_csv('src/data/reviews.csv', index=False)
-
-def add_review(reviews, novo_review):
- novo_review_df = pd.DataFrame([novo_review])
- reviews = pd.concat([reviews, novo_review_df], ignore_index=True)
- save_reviews(reviews)
- return reviews
-
-def analyze_sentiment(text):
- sentiment_pipeline = pipeline("sentiment-analysis", model="nlptown/bert-base-multilingual-uncased-sentiment")
- result = sentiment_pipeline(text)[0]
- label = result['label']
- if label == '1 star' or label == '2 stars':
- return 'Negative'
- elif label == '5 stars' or label == '4 stars':
- return 'Positive'
- else:
- return 'Neutral'
+# Classe para carregar dados
+class DataLoader:
+ @st.cache_resource
+ def load_games():
+ jogos = pd.read_csv("src/data/processed_data.csv") # Usando o arquivo enviado
+ jogos['id'] = range(1, len(jogos) + 1)
+ return jogos
+
+ @staticmethod
+ def load_reviews():
+ return pd.read_sql(reviews_table.select(), con=engine)
+
+# Classe para análise de sentimento
+class SentimentAnalyzer:
+ def __init__(self):
+ self.pipeline = pipeline("sentiment-analysis", model="nlptown/bert-base-multilingual-uncased-sentiment")
+
+ def analyze_sentiment(self, text):
+ result = self.pipeline(text)[0]
+ label = result['label']
+ if label in ['1 star', '2 stars']:
+ return 'Negative'
+ elif label in ['5 stars', '4 stars']:
+ return 'Positive'
+ else:
+ return 'Neutral'
+
+# Classe para visualização de dados
+class DataVisualizer:
+ @staticmethod
+ def plot_avg_game_ratings_plotly(reviews, jogos):
+ if not reviews.empty:
+ reviews['nota'] = reviews['nota'] / 2
+ avg_ratings = reviews.groupby('jogo_id')['nota'].mean()
+ avg_ratings = avg_ratings.to_frame().join(jogos.set_index('id')['title']).rename(columns={'nota': 'avg_rating', 'title': 'game_title'})
+ avg_ratings = avg_ratings.sort_values('avg_rating', ascending=False)
+
+ fig = go.Figure()
+
+ for index, row in avg_ratings.iterrows():
+ fig.add_trace(go.Bar(
+ x=[row['game_title']],
+ y=[row['avg_rating']],
+ text=[f"{'★' * int(round(row['avg_rating']))} ({row['avg_rating']:.1f})"],
+ textposition='auto',
+ ))
+
+ fig.update_layout(
+ title='Média de Notas por Jogo (em estrelas)',
+ xaxis_title='Jogos',
+ yaxis_title='Nota Média',
+ yaxis=dict(range=[0,5]),
+ plot_bgcolor='rgba(0,0,0,0)'
+ )
+
+ return fig
+ else:
+ return None
+
+# Classe Facade
+class GameReviewFacade:
+ def __init__(self):
+ self.jogos = DataLoader.load_games()
+ self.reviews = DataLoader.load_reviews()
+ self.sentiment_analyzer = SentimentAnalyzer()
+
+ def add_review(self, novo_review):
+ session.execute(reviews_table.insert().values(novo_review))
+ session.commit()
+ self.reviews = DataLoader.load_reviews() # Recarregar as reviews do banco de dados
+ return self.reviews
+
+ def analyze_sentiment(self, comentario):
+ return self.sentiment_analyzer.analyze_sentiment(comentario)
+
+ def plot_avg_ratings(self):
+ return DataVisualizer.plot_avg_game_ratings_plotly(self.reviews, self.jogos)
+
+ def get_reviews_with_game_names(self):
+ reviews = self.reviews.copy()
+ reviews = reviews.merge(self.jogos[['id', 'title']], left_on='jogo_id', right_on='id', how='left')
+ reviews = reviews.drop(columns=['id_y'])
+ reviews = reviews.rename(columns={'id_x': 'id', 'title': 'jogo'})
+ return reviews
def main():
st.title("Avaliação de Jogos")
st.sidebar.markdown("# Reviews 🧠")
+
+ load_custom_css()
+ facade = GameReviewFacade()
- jogos = load_data()
- reviews = load_reviews()
-
- # Autenticação simples
usuario = st.text_input("Nome do Usuário")
if not usuario:
st.warning("Por favor, insira um nome de usuário para continuar.")
st.stop()
- # Mapeamento de jogos
- jogos_dict = jogos.set_index('id')['title'].to_dict()
+ jogos_dict = facade.jogos.set_index('id')['title'].to_dict()
- # Seleção do jogo
jogo_id = st.selectbox(
"Selecione um jogo para avaliar:",
- jogos['id'],
+ facade.jogos['id'],
format_func=lambda x: jogos_dict[x]
)
- # Entrada para avaliação
nota = st.slider("Nota", min_value=0, max_value=10, value=5)
comentario = st.text_area("Comentário")
favorito = st.checkbox("Favoritar este jogo")
if st.button("Analisar Sentimento"):
- sentimento = analyze_sentiment(comentario)
+ sentimento = facade.analyze_sentiment(comentario)
if sentimento == 'Positive':
st.success("O sentimento do comentário é positivo!")
elif sentimento == 'Negative':
@@ -112,9 +147,8 @@ def main():
else:
st.info("O sentimento do comentário é neutro.")
- # Submissão de avaliação
if st.button("Enviar Avaliação"):
- sentimento = analyze_sentiment(comentario)
+ sentimento = facade.analyze_sentiment(comentario)
novo_review = {
"jogo_id": jogo_id,
"usuario": usuario,
@@ -123,23 +157,15 @@ def main():
"favorito": favorito,
"sentimento": sentimento # Adiciona o sentimento ao review
}
- reviews = add_review(reviews, novo_review)
+ facade.add_review(novo_review)
st.success("Avaliação enviada com sucesso!")
- # Visualização das avaliações
- if st.checkbox("Ver avaliações existentes"):
- avaliacoes_filtradas = reviews[reviews['jogo_id'] == jogo_id]
- if avaliacoes_filtradas.empty:
- st.write("Ainda não há avaliações para este jogo.")
- else:
- # Adiciona a coluna de sentimento, se não existir
- if 'sentimento' not in avaliacoes_filtradas.columns:
- avaliacoes_filtradas['sentimento'] = avaliacoes_filtradas['comentario'].apply(analyze_sentiment)
- save_reviews(reviews) # Salva as mudanças no arquivo
- st.write(avaliacoes_filtradas)
-
+ if st.button("Ver Reviews Existentes"):
+ reviews_with_names = facade.get_reviews_with_game_names()
+ st.write(reviews_with_names.head())
+
if st.button("Mostrar Gráfico com Plotly"):
- fig = plot_avg_game_ratings_plotly(reviews, jogos)
+ fig = facade.plot_avg_ratings()
st.plotly_chart(fig)
if __name__ == "__main__":
diff --git a/requirements.txt b/requirements.txt
index fa4e029..af343ff 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -13,4 +13,5 @@ psycopg2
openai
pytest
bcrypt
-faiss-cpu
\ No newline at end of file
+faiss-cpu
+torch
\ No newline at end of file
diff --git a/src/data/processed_data_test.csv b/src/data/processed_data_test.csv
new file mode 100644
index 0000000..1510540
--- /dev/null
+++ b/src/data/processed_data_test.csv
@@ -0,0 +1,5 @@
+title,original_price,popular_tags
+"Game A","$19.99","['Action', 'Adventure']"
+"Game B","$29.99","['RPG', 'Fantasy']"
+"Game C","Free","['Puzzle', 'Indie']"
+"Game D","$14.99","['Strategy', 'Multiplayer']"
\ No newline at end of file
diff --git a/src/utils/__pycache__/globals.cpython-311.pyc b/src/utils/__pycache__/globals.cpython-311.pyc
index b76f051..d79254d 100644
Binary files a/src/utils/__pycache__/globals.cpython-311.pyc and b/src/utils/__pycache__/globals.cpython-311.pyc differ
diff --git a/utils.py b/utils.py
index a27201e..13f7ebd 100644
--- a/utils.py
+++ b/utils.py
@@ -3,7 +3,7 @@
from sqlalchemy.orm import sessionmaker
# Configuração da Conexão com o BD
-DATABASE_URL = "postgresql://root:wRoNcAkjnwCvGRdxD2OKAeSevhOLwJ5b@dpg-cq6i442ju9rs73e8bleg-a.oregon-postgres.render.com/loginbd_fg6e"
+DATABASE_URL = "postgresql://mygamehub:XnDyt3Xa8O66bmE7Jbc3ly6zZ3f4eiGH@dpg-cqlnivdumphs7397s8k0-a.oregon-postgres.render.com/loginbd_6tj3"
engine = create_engine(DATABASE_URL)
Session = sessionmaker(bind=engine)
session = Session()
@@ -13,9 +13,7 @@
users = Table('users', metadata,
Column('id', Integer, primary_key=True),
Column('username', String, unique=True),
- Column('password', String),
- Column('name', String),
- Column('gender', String))
+ Column('password', String),)
# Tabela profiles
profiles = Table('profiles', metadata,