Skip to content

Commit

Permalink
Refactor get_statements (#161)
Browse files Browse the repository at this point in the history
* Add AuthenticatedHomePage

* Create MenuPage

* Create CheckingAccountMenu

* Create CheckingAccountStatementsPage

* Add TextPage and SoupPage

* Rename tests

* Add CheckingAccountFullStatement
  • Loading branch information
lucasrcezimbra committed Dec 29, 2021
1 parent b841902 commit 97318c4
Show file tree
Hide file tree
Showing 10 changed files with 372 additions and 46 deletions.
57 changes: 23 additions & 34 deletions pyitau/main.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import re

import requests
from bs4 import BeautifulSoup

from pyitau.pages import FirstRouterPage, PasswordPage, SecondRouterPage
from pyitau.pages import (AuthenticatedHomePage, CheckingAccountFullStatement,
CheckingAccountMenu, CheckingAccountStatementsPage,
FirstRouterPage, MenuPage, PasswordPage,
SecondRouterPage)

ROUTER_URL = 'https://internetpf5.itau.com.br/router-app/router'

Expand Down Expand Up @@ -37,39 +37,28 @@ def authenticate(self):
self._authenticate9()

def get_statements(self):
op = self._home.find('div', class_='logo left').find('a').attrs['data-op']
headers = {'op': self._home.op, 'segmento': 'VAREJO'}

headers = {'op': op, 'segmento': 'VAREJO'}
response = self._session.post(ROUTER_URL, headers=headers)
op2 = re.search(
'urlBox : "(.*?)".*seletorContainer : "#boxContaCorrente",',
response.text,
flags=re.DOTALL,
).group(1)

response = self._session.post(ROUTER_URL, headers={'op': op2})
op3 = re.search(
'urlBox : "(.*?)".*seletorContainer : ".conteudoBoxContaCorrente",',
response.text,
flags=re.DOTALL,
).group(1)

response = self._session.post(ROUTER_URL, headers={'op': op3})
soup = BeautifulSoup(response.text, features='html.parser')
op4 = soup.find('a').attrs['data-op']

response = self._session.post(ROUTER_URL, headers={'op': op4})
pattern = 'function consultarLancamentosPorPeriodo.*' \
'"periodoConsulta" : parametrosPeriodo.*' \
'url = "(.*?)";'
op5 = re.search(
pattern,
response.text,
flags=re.DOTALL,
).group(1)
menu = MenuPage(response.text)

response = self._session.post(ROUTER_URL, headers={'op': menu.checking_account_op})
account_menu = CheckingAccountMenu(response.text)

response = self._session.post(ROUTER_URL, headers={'op': account_menu.statements_op})
statements_page = CheckingAccountStatementsPage(response.text)

response = self._session.post(
ROUTER_URL,
headers={'op': statements_page.full_statement_op},
)
full_statement_page = CheckingAccountFullStatement(response.text)

response = self._session.post(
ROUTER_URL, data={'periodoConsulta': 90}, headers={'op': op5})
ROUTER_URL,
data={'periodoConsulta': 90},
headers={'op': full_statement_page.filter_statements_op},
)
return response.json()

def _authenticate2(self):
Expand Down Expand Up @@ -138,4 +127,4 @@ def _authenticate9(self):
}

response = self._session.post(ROUTER_URL, headers=headers, data=data)
self._home = BeautifulSoup(response.text, features='html.parser')
self._home = AuthenticatedHomePage(response.text)
70 changes: 58 additions & 12 deletions pyitau/pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,23 @@
from bs4 import BeautifulSoup


class FirstRouterPage:
class TextPage:
def __init__(self, response_text):
self._text = response_text


class SoupPage(TextPage):
def __init__(self, response_text):
super().__init__(response_text)
self._soup = BeautifulSoup(self._text, features='html.parser')


class FirstRouterPage(TextPage):
"""
Primeira página após enviar o formulário de Agência e Conta.
Utilizada para extrair do HTML informações que serão necessárias nas
próximas requisições.
"""
def __init__(self, response_text):
self._text = response_text

@property
def auth_token(self):
"""
Expand Down Expand Up @@ -40,15 +48,12 @@ def perform_request(self):
return re.search(r'router.performRequest\("(.*?)", ', self._text).group(1)


class SecondRouterPage:
class SecondRouterPage(TextPage):
"""
Segunda página após enviar o formulário de Agência e Conta.
Também utilizada para extrair do HTML informações que serão necessárias nas
próximas requisições.
"""
def __init__(self, response_text):
self._text = response_text

@property
def op_sign_command(self):
return re.search('__opSignCommand = "(.*?)";', self._text).group(1)
Expand All @@ -65,15 +70,12 @@ def guardiao_cb(self):
).group(1)


class PasswordPage:
class PasswordPage(SoupPage):
"""
Página do teclado da senha. Contém 2 dígitos por botão.
Por baixo dos panos cada botão representa uma letra.
A combinação das letras deve ser enviada na próxima requisição.
"""
def __init__(self, response_text):
self._soup = BeautifulSoup(response_text, features='html.parser')

@property
def op(self):
"""
Expand Down Expand Up @@ -111,3 +113,47 @@ def letter_password(self, password):
"""
mapper = self._get_password_mapper()
return ''.join(mapper[n] for n in password)


class AuthenticatedHomePage(SoupPage):
"""
Primeira página após o login
"""
@property
def op(self):
return self._soup.find('div', class_='logo left').find('a').attrs['data-op']


class MenuPage(TextPage):
@property
def checking_account_op(self):
return re.search(
'urlBox : "(.*?)".*seletorContainer : "#boxContaCorrente",',
self._text,
flags=re.DOTALL,
).group(1)


class CheckingAccountMenu(TextPage):
@property
def statements_op(self):
return re.search(
'urlBox : "(.*?)".*seletorContainer : ".conteudoBoxContaCorrente",',
self._text,
flags=re.DOTALL,
).group(1)


class CheckingAccountStatementsPage(SoupPage):
@property
def full_statement_op(self):
return self._soup.find('a').attrs['data-op']


class CheckingAccountFullStatement(TextPage):
@property
def filter_statements_op(self):
pattern = 'function consultarLancamentosPorPeriodo.*' \
'"periodoConsulta" : parametrosPeriodo.*' \
'url = "(.*?)";'
return re.search(pattern, self._text, flags=re.DOTALL).group(1)
7 changes: 7 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,10 @@ def response_authenticate8():
with open('./tests/responses/authenticate8.html') as file:
body = file.read()
return body


@pytest.fixture
def response_menu():
with open('./tests/responses/menu.html') as file:
body = file.read()
return body
42 changes: 42 additions & 0 deletions tests/responses/checking_account_full_statement.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<script>
function consultarLancamentosPorPeriodo(parametrosPeriodo,
opcaoSelecionadaPeriodo) {
var parametros = {};
var url;

$('#extrato-grid-lancamentos, #extrato-grid-lancamentos-futuros-entrada, #extrato-grid-lancamentos-futuros-saida').empty();
$('#extrato-btn-ver-mais-lancamentos').empty();

if (filtro.periodoConsulta !== parametrosPeriodo) {

if (opcaoSelecionadaPeriodo === "mesCompleto") {
parametros = {
"mesCompleto" : parametrosPeriodo
};
filtro.mesCompleto = parametrosPeriodo;
filtro.periodoConsulta = null;
url = "RANDOM_STRING=;";

} else {

filtro.mesCompleto = null;
filtro.periodoConsulta = parametrosPeriodo;

// $('#extrato-grid-lancamentos-futuros').empty();
$('#extrato-btn-ver-mais-lancamentos-futuros').empty();

$('#extrato-grid-outros-lancamentos').empty();

parametros = {
"periodoConsulta" : parametrosPeriodo
};
url = "PYITAU_OP_filter_statements=;";

armazenaValoresFiltro("periodoVisualizacao", filtroLancamentos.filtroSelecionado().periodoConsulta);
}
adobeFiltroPeriodoExtrato(filtro, true);
}
checkCookieSalvarFiltros() && setCookies("filtro_periodo_" + iDPF, filtroLancamentos.filtroSelecionado().periodoConsulta);
filtraExtrato(parametros, url);
}
</script>
92 changes: 92 additions & 0 deletions tests/responses/menu.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
var boxHome = [


{
urlBox : "PYITAU_OP_ContaCorrente",
seletorContainer : "#boxContaCorrente",
isAberto : ( verificarSituacaoBoxAberto("boxContaCorrente") && 'false' == 'false' ) || ( verificaSituacaoBoxApp() && 'false' == 'true'),
timeout: 90000,
onComplete: function(){
_orquestradorLightBox.atualizar('contacorrente');
}
},



{
urlBox : "PYITAU_OP_Pagamentos",
seletorContainer : "#boxPagamentos",
data: {
linkLeitorCelular: false
}
},
{
urlBox : "PYITAU_OP_Transferencias",
seletorContainer : "#boxTransferencias"
},
{
urlBox : "PYITAU_OP_Pix",
seletorContainer : "#boxPix"
},
{
urlBox : "PYITAU_OP_OpenBanking",
seletorContainer : "#boxOpenBanking"
},
{
urlBox : "PYITAU_OP_Credito",
seletorContainer : "#boxCredito"
},

{
urlBox : "PYITAU_OP_Central",
seletorContainer : "#boxCentral",
showLoader : false,
tratarError: false,
onComplete: function(){
_orquestradorLightBox.atualizar('centraltarefas');
}

},
{
urlBox : "PYITAU_OP_MeuBanner",
seletorContainer : "#boxMeuBanner",
showLoader : false,
tratarError: false
},
{
urlBox : "PYITAU_OP_Cartoes",
seletorContainer : "#boxCartoes",
isAberto : false,
tratarError: false
},
/*
{
urlBox : "PYITAU_OP_Recarga",
seletorContainer : "#boxRecarga"
},*/

{
urlBox : "PYITAU_OP_Documentos",

seletorContainer : "#boxDocumentos",
onComplete: function(){
carregaDataLayerHome();
_orquestradorLightBox.atualizar('documentos');
}
},



{
urlBox : "PYITAU_OP_HomeCarrinhoPF",
seletorContainer : "#boxHomeCarrinhoPF",
tratarError: false,
data: {
isHome: true,
localExibicao : 'HOME_PF'
}
},


{ }
];
40 changes: 40 additions & 0 deletions tests/test_authenticated_home_page.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import pytest
from bs4 import BeautifulSoup

from pyitau.pages import AuthenticatedHomePage


@pytest.fixture
def response():
return """
<div class="logo left">
<a
data-op="PYITAU_OP"
href=""
id="HomeLogo"
onclick="GA.pushHeader('logoItau');"
title="Home"
>
<img
alt="Logo Itaú"
height="50"
src="https://estatico.itau.com.br/.../logo-itau.png"
width="50"
/>
</a>
</div>
"""


@pytest.fixture
def page(response):
return AuthenticatedHomePage(response)


def test_init(response):
page = AuthenticatedHomePage(response)
assert page._soup == BeautifulSoup(response, features='html.parser')


def test_op(page):
assert page.op == 'PYITAU_OP'
19 changes: 19 additions & 0 deletions tests/test_checking_account_full_statement_page.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import pytest

from pyitau.pages import CheckingAccountFullStatement


@pytest.fixture
def response():
with open('./tests/responses/checking_account_full_statement.html') as file:
body = file.read()
return body


@pytest.fixture
def page(response):
return CheckingAccountFullStatement(response)


def test_op(page):
assert page.filter_statements_op == 'PYITAU_OP_filter_statements=;'

0 comments on commit 97318c4

Please sign in to comment.