From 2246d7ba333e19c8767c26ba4c645cc444d1fc43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLuis=E2=80=9D?= Date: Thu, 1 Aug 2024 08:41:56 -0300 Subject: [PATCH 01/28] =?UTF-8?q?Implementando=20testes=20b=C3=A1sicos=20p?= =?UTF-8?q?ara=20verifica=C3=A7=C3=A3o=20de=20erros?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +++- pages/tests/test_mario.py | 5 +++++ src/utils/__pycache__/globals.cpython-311.pyc | Bin 233 -> 236 bytes 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 pages/tests/test_mario.py 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/pages/tests/test_mario.py b/pages/tests/test_mario.py new file mode 100644 index 0000000..d1ae229 --- /dev/null +++ b/pages/tests/test_mario.py @@ -0,0 +1,5 @@ +from streamlit.testing.v1 import AppTest + +def test_profile(): + at = AppTest.from_file("../🤖 Mario.py").run(timeout=20) + assert not at.error \ 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 b76f0516092c621e4a7076fed3194990aba5d8bc..d79254df7ed28f0823055bf6e82d73e240248cd6 100644 GIT binary patch delta 98 zcmaFK_=b^pIWI340}%Y{nw#c4kyp#cx;R_6xGXs_Ik`kPA~Pq?&Dh)BI5;sdvoI+* x%h)$3#Mmh(DcjAfBE7)LtkT8T(zz(BBCR4Lr@*&rVnHsCa|1I77O?;g1pqaKA9nx% delta 95 zcmaFE_>z%#IWI340}xDcoSWt_kyp#oqBvW(peR2}*FQWo%{@CgAS)uhJTX74B*-N= uEX3F;Cn?*_%fl};w>U4wFU!Zo%gZG^C@?G3XJT=8Z From a4f9c9b2b36a7dc8a501bb452fc37b476fd2fe98 Mon Sep 17 00:00:00 2001 From: Vlaady Date: Thu, 1 Aug 2024 08:50:32 -0300 Subject: [PATCH 02/28] close issue#66 --- "pages/\360\237\224\216 Busca.py" | 83 ++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 30 deletions(-) diff --git "a/pages/\360\237\224\216 Busca.py" "b/pages/\360\237\224\216 Busca.py" index e15a7a4..72826c8 100644 --- "a/pages/\360\237\224\216 Busca.py" +++ "b/pages/\360\237\224\216 Busca.py" @@ -1,24 +1,54 @@ import pandas as pd import streamlit as st -def clean_price(price): - if price.lower() == 'free': - return 0.0 - return float(price.replace('$', '').replace(',', '')) +class GameSearchFacade: + def __init__(self): + self.data = self.load_data() + self.all_tags = self.extract_all_tags() -@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 + @staticmethod + def clean_price(price): + if price.lower() == 'free': + return 0.0 + return float(price.replace('$', '').replace(',', '')) -data = load_data() + @staticmethod + @st.cache_data + def load_data(): + data = pd.read_csv('src/data/processed_data.csv') + 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 -# 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)) + 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): + if selected_tags is None: + selected_tags = [] + + results = self.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))] + + if min_price is not None and max_price is not None: + results = results[results['original_price'].between(min_price, max_price)] + + return results + + def get_all_tags(self): + return self.all_tags + +# Instanciar o facade +game_search = GameSearchFacade() + +# Configurar a interface do usuário page_bg_img = ''' ', 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 - -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' - -# 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", "sentimento"]) - -# 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 +# Classe para carregar dados +class DataLoader: + @staticmethod + def load_games(): + 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 + + @staticmethod + def load_reviews(): + try: + return pd.read_csv('src/data/reviews.csv') + except FileNotFoundError: + return pd.DataFrame(columns=["jogo_id", "usuario", "nota", "comentario", "favorito", "sentimento"]) + + @staticmethod + def save_reviews(reviews): + reviews.to_csv('src/data/reviews.csv', index=False) + +# 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): + novo_review_df = pd.DataFrame([novo_review]) + self.reviews = pd.concat([self.reviews, novo_review_df], ignore_index=True) + DataLoader.save_reviews(self.reviews) + return self.reviews + + def analyze_sentiment(self, comentario): + return self.sentiment_analyzer.analyze_sentiment(comentario) + + def get_reviews_for_game(self, jogo_id): + return self.reviews[self.reviews['jogo_id'] == jogo_id] + + def plot_avg_ratings(self): + return DataVisualizer.plot_avg_game_ratings_plotly(self.reviews, self.jogos) 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") - # Análise de Sentimento 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': @@ -113,9 +129,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, @@ -124,19 +139,18 @@ def main(): "favorito": favorito, "sentimento": sentimento } - 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] + avaliacoes_filtradas = facade.get_reviews_for_game(jogo_id) if avaliacoes_filtradas.empty: st.write("Ainda não há avaliações para este jogo.") else: st.write(avaliacoes_filtradas) 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__": From 736c993c6905ada70804959d03f1ab8f96a7ce7e Mon Sep 17 00:00:00 2001 From: milio Date: Thu, 1 Aug 2024 09:45:19 -0300 Subject: [PATCH 05/28] Resolvendo conflitos --- utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils.py b/utils.py index a27201e..a74fb65 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() From 9100dca35bbe63005929405130a81f38a6243866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLuis=E2=80=9D?= Date: Thu, 1 Aug 2024 11:44:40 -0300 Subject: [PATCH 06/28] Adicionando library --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 From 042a214e224ea6844cea9cf61963647fcf784bd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLuis=E2=80=9D?= Date: Mon, 5 Aug 2024 20:46:47 -0300 Subject: [PATCH 07/28] Concertando a branch dev --- login.py | 2 -- "pages/\342\234\250 Wishlist.py" | 14 ++++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/login.py b/login.py index 84d57ae..be73d28 100644 --- a/login.py +++ b/login.py @@ -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/\342\234\250 Wishlist.py" "b/pages/\342\234\250 Wishlist.py" index 2ab6d4c..b1c561e 100644 --- "a/pages/\342\234\250 Wishlist.py" +++ "b/pages/\342\234\250 Wishlist.py" @@ -48,11 +48,21 @@ else: st.error('Por favor, insira o nome de um jogo.') +# Função para remover item da lista de desejos +def remove_wishlist_item(user_id, game_id): + session.execute(wishlist.delete().where(wishlist.c.user_id == user_id).where(wishlist.c.game_id == game_id)) + session.commit() + # 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}") + col1, col2 = st.columns([3, 1]) + col1.write(f"Jogo: {item.game_id}") + if col2.button('Remover', key=item.id): + remove_wishlist_item(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.') + st.write('Sua lista de desejos está vazia.') \ No newline at end of file From cdb5d378d26f58bb1d42a3aa1deb5f67777c80cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLuis=E2=80=9D?= Date: Mon, 5 Aug 2024 20:50:55 -0300 Subject: [PATCH 08/28] Concertando a branch dev --- "pages/\360\237\244\226 Mario.py" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/pages/\360\237\244\226 Mario.py" "b/pages/\360\237\244\226 Mario.py" index cc36f7d..7d547e8 100644 --- "a/pages/\360\237\244\226 Mario.py" +++ "b/pages/\360\237\244\226 Mario.py" @@ -48,7 +48,7 @@ def __init__(self): self.model = ChatOpenAI(temperature=0.2, model="gpt-4o", api_key=KEY) self.chain = self._create_chain() - def _initialize_session_state(self) + 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() From 6059cd8398d1de1cefe986db8a44d8721de1d450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLuis=E2=80=9D?= Date: Mon, 5 Aug 2024 20:54:54 -0300 Subject: [PATCH 09/28] Concertando a branch dev --- utils.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/utils.py b/utils.py index a74fb65..2d69a9d 100644 --- a/utils.py +++ b/utils.py @@ -14,8 +14,6 @@ Column('id', Integer, primary_key=True), Column('username', String, unique=True), Column('password', String), - Column('name', String), - Column('gender', String)) # Tabela profiles profiles = Table('profiles', metadata, From d94143ed0dfd8835379025e3ae54862c673c067d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLuis=E2=80=9D?= Date: Mon, 5 Aug 2024 20:56:48 -0300 Subject: [PATCH 10/28] Concertando a branch dev --- utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils.py b/utils.py index 2d69a9d..13f7ebd 100644 --- a/utils.py +++ b/utils.py @@ -13,7 +13,7 @@ users = Table('users', metadata, Column('id', Integer, primary_key=True), Column('username', String, unique=True), - Column('password', String), + Column('password', String),) # Tabela profiles profiles = Table('profiles', metadata, From 83d02c4dfaa17939385455debef26a6505e1401b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLuis=E2=80=9D?= Date: Tue, 6 Aug 2024 08:00:07 -0300 Subject: [PATCH 11/28] =?UTF-8?q?Reformatando=20c=C3=B3digo=20do=20teste?= =?UTF-8?q?=20unit=C3=A1rio=20da=20feature=20Mario?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/tests/test_mario.py | 10 +++++++--- "pages/\360\237\244\226 Mario.py" | 7 ++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/pages/tests/test_mario.py b/pages/tests/test_mario.py index d1ae229..7e62588 100644 --- a/pages/tests/test_mario.py +++ b/pages/tests/test_mario.py @@ -1,5 +1,9 @@ from streamlit.testing.v1 import AppTest -def test_profile(): - at = AppTest.from_file("../🤖 Mario.py").run(timeout=20) - assert not at.error \ No newline at end of file +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/\360\237\244\226 Mario.py" "b/pages/\360\237\244\226 Mario.py" index 320c032..5458638 100644 --- "a/pages/\360\237\244\226 Mario.py" +++ "b/pages/\360\237\244\226 Mario.py" @@ -8,13 +8,14 @@ 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() @st.cache_resource def load_data(): 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: @@ -47,7 +48,7 @@ def _create_prompt_template(self): return ChatPromptTemplate.from_template(template) def _initialize_model(self): - return ChatOpenAI(temperature=0.2, model="gpt-4o", api_key=KEY) + return ChatOpenAI(temperature=0.2, model="gpt-4o") def _create_chain(self): return ( From f0031edd2a976b93c96608ac582be207aee3e62e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLuis=E2=80=9D?= Date: Tue, 6 Aug 2024 08:13:27 -0300 Subject: [PATCH 12/28] Arrumando erro --- "pages/\360\237\244\226 Mario.py" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/pages/\360\237\244\226 Mario.py" "b/pages/\360\237\244\226 Mario.py" index 0f2f946..a15e581 100644 --- "a/pages/\360\237\244\226 Mario.py" +++ "b/pages/\360\237\244\226 Mario.py" @@ -44,7 +44,7 @@ class Mario: def __init__(self): """Inicializando a instância do Mario, configurando o estado da sessão, o modelo e a cadeia de recuperação.""" self._initialize_session_state() - self.model = ChatOpenAI(temperature=0.2, model="gpt-4o", api_key=KEY) + self.model = ChatOpenAI(temperature=0.2, model="gpt-4o") self.chain = self._create_chain() def _initialize_session_state(self): From 88c02c1a8122d90b50111f484ad3d32914d42d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLuis=E2=80=9D?= Date: Tue, 6 Aug 2024 08:21:31 -0300 Subject: [PATCH 13/28] Arrumando erro --- .github/workflows/main.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 776850c..a1b01e7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,12 +13,19 @@ 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 with: python-version: "3.11" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Run tests + run: | + pytest --ignore=pages/tests/test_mario.py - uses: streamlit/streamlit-app-action@v0.0.3 with: app-path: Home.py \ No newline at end of file From 98bfb2ccb0dc2a0033aba5e470a798bcac70a3dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLuis=E2=80=9D?= Date: Tue, 6 Aug 2024 08:25:30 -0300 Subject: [PATCH 14/28] Arrumando erro --- .github/workflows/main.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a1b01e7..8089b9e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,13 +19,6 @@ jobs: - uses: actions/setup-python@v5 with: python-version: "3.11" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - - name: Run tests - run: | - pytest --ignore=pages/tests/test_mario.py - uses: streamlit/streamlit-app-action@v0.0.3 with: app-path: Home.py \ No newline at end of file From 6950e4aaa29ebc90822666230df145537320c5cf Mon Sep 17 00:00:00 2001 From: milio Date: Tue, 6 Aug 2024 08:33:39 -0300 Subject: [PATCH 15/28] testes da profile e da reviews --- pages/tests/test_profile.py | 21 +++++++++++++++++++++ pages/tests/test_reviews.py | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 pages/tests/test_profile.py create mode 100644 pages/tests/test_reviews.py diff --git a/pages/tests/test_profile.py b/pages/tests/test_profile.py new file mode 100644 index 0000000..dbbb4ff --- /dev/null +++ b/pages/tests/test_profile.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_profile(): + try: + at = AppTest.from_file("../Profile.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_profile() 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() From 2e22a8f9153d0ed8cc5bcc1ca505a571fa7b3cec Mon Sep 17 00:00:00 2001 From: milio Date: Tue, 6 Aug 2024 08:42:27 -0300 Subject: [PATCH 16/28] closeissue#63 --- pages/Profile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/Profile.py b/pages/Profile.py index 581ee59..e252025 100644 --- a/pages/Profile.py +++ b/pages/Profile.py @@ -14,10 +14,10 @@ def plot_avg_game_ratings_plotly(reviews, jogos): avg_ratings = avg_ratings.sort_values('avg_rating', ascending=False) # Criação do gráfico - fig = go.Figure() + fig = st.go.Figure() for index, row in avg_ratings.iterrows(): - fig.add_trace(go.Bar( + fig.add_trace(st.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 From 9046b29db27dc262b798a9b0384a0d9fcfc8f8b4 Mon Sep 17 00:00:00 2001 From: milio Date: Tue, 6 Aug 2024 08:55:04 -0300 Subject: [PATCH 17/28] closeissue#63 --- pages/tests/test_profile.py | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/pages/tests/test_profile.py b/pages/tests/test_profile.py index dbbb4ff..e069e91 100644 --- a/pages/tests/test_profile.py +++ b/pages/tests/test_profile.py @@ -1,21 +1,5 @@ from streamlit.testing.v1 import AppTest -import logging - -# Configuração de logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) def test_profile(): - try: - at = AppTest.from_file("../Profile.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_profile() + at = AppTest.from_file("../Profile.py").run(timeout=50) + assert not at.error, "Erro encontrado durante a execução inicial" From e24f000be5925b8491b2d3eeed068db37ad501a8 Mon Sep 17 00:00:00 2001 From: milio Date: Tue, 6 Aug 2024 09:04:29 -0300 Subject: [PATCH 18/28] closeissue#63 --- pages/tests/test_profile.py | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 pages/tests/test_profile.py diff --git a/pages/tests/test_profile.py b/pages/tests/test_profile.py deleted file mode 100644 index e069e91..0000000 --- a/pages/tests/test_profile.py +++ /dev/null @@ -1,5 +0,0 @@ -from streamlit.testing.v1 import AppTest - -def test_profile(): - at = AppTest.from_file("../Profile.py").run(timeout=50) - assert not at.error, "Erro encontrado durante a execução inicial" From cfa2a1a722c74d5af239efd36d242f8bb6b5d659 Mon Sep 17 00:00:00 2001 From: kauasampa Date: Tue, 6 Aug 2024 09:27:12 -0300 Subject: [PATCH 19/28] close issue#75 --- pages/tests/test_wishlist.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 pages/tests/test_wishlist.py 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() From 939be150a7aef43f95711d47b5aef8d571462964 Mon Sep 17 00:00:00 2001 From: milio Date: Tue, 6 Aug 2024 15:40:43 -0300 Subject: [PATCH 20/28] closeissue#64 --- pages/Profile.py | 186 ++++++++++++++++++++++++----------------------- 1 file changed, 95 insertions(+), 91 deletions(-) diff --git a/pages/Profile.py b/pages/Profile.py index e252025..84e2f87 100644 --- a/pages/Profile.py +++ b/pages/Profile.py @@ -5,32 +5,26 @@ 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 = st.go.Figure() - for index, row in avg_ratings.iterrows(): fig.add_trace(st.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 +32,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 +43,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 + profile_page() From ba3303e4cdabd07f8faa73b728b9dc5735aeba7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLuis=E2=80=9D?= Date: Tue, 6 Aug 2024 20:36:26 -0300 Subject: [PATCH 21/28] =?UTF-8?q?Adicionando=20teste=20de=20login=20do=20u?= =?UTF-8?q?su=C3=A1rio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/tests/test_login.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 pages/tests/test_login.py 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 From cf015dbd99db28aef883ef01d6150580f1b4f018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLuis=E2=80=9D?= Date: Tue, 6 Aug 2024 20:36:41 -0300 Subject: [PATCH 22/28] =?UTF-8?q?Leves=20altera=C3=A7=C3=B5es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Home.py | 2 -- 1 file changed, 2 deletions(-) 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 From bb32ecc5cae60a484ad045ff61848a82c231a255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLuis=E2=80=9D?= Date: Tue, 6 Aug 2024 20:37:46 -0300 Subject: [PATCH 23/28] =?UTF-8?q?Alterando=20l=C3=B3gica=20para=20armazena?= =?UTF-8?q?mento=20dos=20dados=20no=20BD=20online=20e=20visualiza=C3=A7?= =?UTF-8?q?=C3=A3o=20gr=C3=A1fica=20a=20partir=20dele?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "pages/\360\237\247\240 Reviews.py" | 74 ++++++++++++++--------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git "a/pages/\360\237\247\240 Reviews.py" "b/pages/\360\237\247\240 Reviews.py" index be1247a..c50c6c9 100644 --- "a/pages/\360\237\247\240 Reviews.py" +++ "b/pages/\360\237\247\240 Reviews.py" @@ -2,6 +2,27 @@ 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(): @@ -10,22 +31,15 @@ def load_custom_css(): # Classe para carregar dados class DataLoader: - @staticmethod + @st.cache_resource def load_games(): - 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") # Usando o arquivo enviado jogos['id'] = range(1, len(jogos) + 1) return jogos @staticmethod def load_reviews(): - try: - return pd.read_csv('src/data/reviews.csv') - except FileNotFoundError: - return pd.DataFrame(columns=["jogo_id", "usuario", "nota", "comentario", "favorito", "sentimento"]) - - @staticmethod - def save_reviews(reviews): - reviews.to_csv('src/data/reviews.csv', index=False) + return pd.read_sql(reviews_table.select(), con=engine) # Classe para análise de sentimento class SentimentAnalyzer: @@ -82,30 +96,23 @@ def __init__(self): self.sentiment_analyzer = SentimentAnalyzer() def add_review(self, novo_review): - novo_review_df = pd.DataFrame([novo_review]) - self.reviews = pd.concat([self.reviews, novo_review_df], ignore_index=True) - DataLoader.save_reviews(self.reviews) + 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 get_reviews_for_game(self, jogo_id): - return self.reviews[self.reviews['jogo_id'] == jogo_id] - def plot_avg_ratings(self): return DataVisualizer.plot_avg_game_ratings_plotly(self.reviews, self.jogos) -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' + 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") @@ -153,17 +160,10 @@ def main(): facade.add_review(novo_review) st.success("Avaliação enviada com sucesso!") - if st.checkbox("Ver avaliações existentes"): - avaliacoes_filtradas = facade.get_reviews_for_game(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 = facade.plot_avg_ratings() st.plotly_chart(fig) From dbebf98b560bc367a166d6460f598cb22094a2b1 Mon Sep 17 00:00:00 2001 From: Vlaady Date: Wed, 7 Aug 2024 15:01:00 -0300 Subject: [PATCH 24/28] close issue#61 --- pages/tests/test_busca.py | 4 ++++ "pages/\360\237\223\260 Journal.py" | 8 ++++---- src/data/processed_data_test.csv | 5 +++++ 3 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 pages/tests/test_busca.py create mode 100644 src/data/processed_data_test.csv 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/\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/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 From f707a4b0c63374b64ab27509f552a7111a3972a3 Mon Sep 17 00:00:00 2001 From: Vlaady Date: Wed, 7 Aug 2024 15:08:30 -0300 Subject: [PATCH 25/28] close issue#62 --- "pages/\360\237\224\216 Busca.py" | 140 +++++++++++++++++------------- 1 file changed, 81 insertions(+), 59 deletions(-) diff --git "a/pages/\360\237\224\216 Busca.py" "b/pages/\360\237\224\216 Busca.py" index 72826c8..b2b5df9 100644 --- "a/pages/\360\237\224\216 Busca.py" +++ "b/pages/\360\237\224\216 Busca.py" @@ -3,7 +3,7 @@ class GameSearchFacade: def __init__(self): - self.data = self.load_data() + self.data = self.load_and_prepare_data() self.all_tags = self.extract_all_tags() @staticmethod @@ -12,10 +12,19 @@ def clean_price(price): 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(): - data = pd.read_csv('src/data/processed_data.csv') + 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(',')] @@ -26,69 +35,82 @@ 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): - if selected_tags is None: - selected_tags = [] - - results = self.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))] - - if min_price is not None and max_price is not None: - results = results[results['original_price'].between(min_price, max_price)] - + 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 -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(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())) -) - -# Campo de seleção para filtrar por tags populares -selected_tags = st.multiselect('Selecione as tags populares:', game_search.get_all_tags()) - -# Buscar jogos com base na entrada do usuário -results = game_search.search_games(query, selected_tags, min_price, max_price) - -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.") +configure_interface(game_search) \ No newline at end of file From c07ba37c3404a4e8909f39ec3820c5522511300e Mon Sep 17 00:00:00 2001 From: kauasampa Date: Wed, 7 Aug 2024 15:19:36 -0300 Subject: [PATCH 26/28] close issue#74 --- "pages/\342\234\250 Wishlist.py" | 102 ++++++++++++++++++------------- 1 file changed, 60 insertions(+), 42 deletions(-) diff --git "a/pages/\342\234\250 Wishlist.py" "b/pages/\342\234\250 Wishlist.py" index b1c561e..aae3f42 100644 --- "a/pages/\342\234\250 Wishlist.py" +++ "b/pages/\342\234\250 Wishlist.py" @@ -4,8 +4,16 @@ # 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() + +try: + engine = create_engine(DATABASE_URL) + metadata = MetaData() + metadata.create_all(engine) + Session = sessionmaker(bind=engine) + session = Session() + #st.write("Conexão com o banco de dados estabelecida com sucesso.") +except Exception as e: + st.error(f"Erro ao conectar ao banco de dados: {e}") # Tabela users users = Table('users', metadata, @@ -19,50 +27,60 @@ Column('user_id', Integer), Column('game_id', String)) -metadata.create_all(engine) - -Session = sessionmaker(bind=engine) -session = Session() +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() -# 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() +def get_logged_in_user(): + username = st.session_state.username + user = session.query(users).filter(users.c.username == username).first() + if user: + st.write(f"Usuário logado: {user.username}") + return user -# Obter informações do usuário logado -username = st.session_state.username -user = session.query(users).filter(users.c.username == username).first() +def add_to_wishlist(user_id, 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!') -# Página da Lista de Desejos -st.title('Minha Lista de Desejos') -st.sidebar.markdown("# Wishlist ✨") +def remove_from_wishlist(user_id, game_id): + session.execute(wishlist.delete().where(wishlist.c.user_id == user_id).where(wishlist.c.game_id == game_id)) + session.commit() -# 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!') +def display_wishlist(user_id): + 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: + col1, col2 = st.columns([3, 1]) + col1.write(f"Jogo: {item.game_id}") + if col2.button('Remover', key=item.id): + 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.error('Por favor, insira o nome de um jogo.') + st.write('Sua lista de desejos está vazia.') -# Função para remover item da lista de desejos -def remove_wishlist_item(user_id, game_id): - session.execute(wishlist.delete().where(wishlist.c.user_id == user_id).where(wishlist.c.game_id == game_id)) - session.commit() +def main(): + check_user_logged_in() + user = get_logged_in_user() + + 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: + add_to_wishlist(user.id, game_id) + else: + st.error('Por favor, insira o nome de um jogo.') + + display_wishlist(user.id) + else: + st.error("Não foi possível recuperar o usuário logado.") -# 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: - col1, col2 = st.columns([3, 1]) - col1.write(f"Jogo: {item.game_id}") - if col2.button('Remover', key=item.id): - remove_wishlist_item(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.') \ No newline at end of file +if __name__ == "__main__": + main() From 3748bdef71dfc995bec401b2ab5bdecbc9f240ac Mon Sep 17 00:00:00 2001 From: kauasampa Date: Wed, 7 Aug 2024 15:41:46 -0300 Subject: [PATCH 27/28] close issue#70 --- "pages/\342\234\250 Wishlist.py" | 179 ++++++++++++++++--------------- 1 file changed, 93 insertions(+), 86 deletions(-) diff --git "a/pages/\342\234\250 Wishlist.py" "b/pages/\342\234\250 Wishlist.py" index aae3f42..7b3493f 100644 --- "a/pages/\342\234\250 Wishlist.py" +++ "b/pages/\342\234\250 Wishlist.py" @@ -1,86 +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://mygamehub:XnDyt3Xa8O66bmE7Jbc3ly6zZ3f4eiGH@dpg-cqlnivdumphs7397s8k0-a.oregon-postgres.render.com/loginbd_6tj3" - -try: - engine = create_engine(DATABASE_URL) - metadata = MetaData() - metadata.create_all(engine) - Session = sessionmaker(bind=engine) - session = Session() - #st.write("Conexão com o banco de dados estabelecida com sucesso.") -except Exception as e: - st.error(f"Erro ao conectar ao banco de dados: {e}") - -# 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)) - -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 get_logged_in_user(): - username = st.session_state.username - user = session.query(users).filter(users.c.username == username).first() - if user: - st.write(f"Usuário logado: {user.username}") - return user - -def add_to_wishlist(user_id, 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!') - -def remove_from_wishlist(user_id, game_id): - session.execute(wishlist.delete().where(wishlist.c.user_id == user_id).where(wishlist.c.game_id == game_id)) - session.commit() - -def display_wishlist(user_id): - 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: - col1, col2 = st.columns([3, 1]) - col1.write(f"Jogo: {item.game_id}") - if col2.button('Remover', key=item.id): - 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.') - -def main(): - check_user_logged_in() - user = get_logged_in_user() - - 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: - add_to_wishlist(user.id, game_id) - else: - st.error('Por favor, insira o nome de um jogo.') - - display_wishlist(user.id) - else: - st.error("Não foi possível recuperar o usuário logado.") - -if __name__ == "__main__": - main() +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() From b30a35a85c3a410c2750d7a8621054614d31b113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CLuis=E2=80=9D?= Date: Wed, 7 Aug 2024 16:06:24 -0300 Subject: [PATCH 28/28] =?UTF-8?q?Reparando=20=C3=BAltimos=20bugs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/Profile.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pages/Profile.py b/pages/Profile.py index 84e2f87..fc97bb4 100644 --- a/pages/Profile.py +++ b/pages/Profile.py @@ -1,5 +1,6 @@ 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 @@ -10,9 +11,9 @@ def plot_avg_game_ratings_plotly(reviews, jogos): 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 = st.go.Figure() + fig = go.Figure() for index, row in avg_ratings.iterrows(): - fig.add_trace(st.go.Bar( + fig.add_trace(go.Bar( x=[row['game_title']], y=[row['avg_rating']], text=[f"{'★' * int(round(row['avg_rating']))} ({row['avg_rating']:.1f})"], @@ -157,4 +158,4 @@ def profile_page(): account_settings(user) if __name__ == "__main__": - profile_page() + profile_page() \ No newline at end of file