diff --git a/.gitignore b/.gitignore index d5fc83d..72a0041 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.pyc *.coverage +.eggs dist build packtrack.egg-info diff --git a/.travis.yml b/.travis.yml index 8e1e41c..cfc7264 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,7 @@ language: python -before_install: pip install -r requirements.txt +before_install: + - python setup.py install + - pip install -r requirements-dev.txt -script: nosetests \ No newline at end of file +script: nosetests diff --git a/packtrack/__init__.py b/packtrack/__init__.py index 80d4852..969f14b 100755 --- a/packtrack/__init__.py +++ b/packtrack/__init__.py @@ -1,6 +1,6 @@ -from correios import EncomendaRepository -from royal import RoyalMail -from dhl_gm import DhlGmTracker +from .correios import EncomendaRepository +from .royal import RoyalMail +from .dhl_gm import DhlGmTracker class Correios(object): diff --git a/packtrack/correios.py b/packtrack/correios.py index 4514751..897147b 100644 --- a/packtrack/correios.py +++ b/packtrack/correios.py @@ -15,7 +15,7 @@ def get(self, numero, auth=None): return func(numero, **kwargs) def _init_scraper(self, backend): - from scraping import CorreiosWebsiteScraper, CorreiosRastroService + from .scraping import CorreiosWebsiteScraper, CorreiosRastroService if backend is None: backend = 'www2' @@ -34,10 +34,9 @@ def __init__(self, numero): self.status = [] def adicionar_status(self, status): - d = datetime self.status.append(status) t_format = self.validar_data(status.data) - self.status.sort(lambda x, y: 1 if d.strptime(x.data, t_format) > d.strptime(y.data, t_format) else -1) + self.status.sort(key=lambda s: datetime.strptime(s.data, t_format)) def validar_data(self, data): if re.match('^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$', data): diff --git a/packtrack/scraping.py b/packtrack/scraping.py index fc0c3b0..5350769 100755 --- a/packtrack/scraping.py +++ b/packtrack/scraping.py @@ -1,15 +1,15 @@ import os import re -from HTMLParser import HTMLParser +from html import unescape -from BeautifulSoup import BeautifulSoup +from bs4 import BeautifulSoup import requests from requests.exceptions import RequestException from zeep import Client as Zeep from zeep.cache import InMemoryCache from zeep.transports import Transport -from correios import Encomenda, Status +from .correios import Encomenda, Status class CorreiosWebsiteScraper(object): @@ -46,49 +46,36 @@ def get_encomenda_info(self, numero): except RequestException: return None - html = response.content - + html = response.text if html: - try: - html = html.decode('latin-1') - except UnicodeDecodeError: - pass encomenda = Encomenda(numero) for status in self._get_all_status_from_html(html): encomenda.adicionar_status(status) return encomenda def _text(self, value): - value = BeautifulSoup(value.strip()).text - return value.replace(' ', ' ') + text = BeautifulSoup(value.strip(), 'html.parser').text + return re.sub(r'[\s\t]+', ' ', text) def _get_all_status_from_html(self, html): status = [] - html_parser = HTMLParser() - if ").*', html, re.S) - if not html_info: - return status - - table = html_info.group(1) - soup = BeautifulSoup(table) - - for tr in soup.table: + # O bs4 converte o   para \xa0 ao invés de espaço. + clean_html = html.replace(' ', ' ') + soup = BeautifulSoup(clean_html, 'html.parser') + for tr in soup.select('table.sro tr'): try: tds = tr.findAll('td') except AttributeError: continue for td in tds: - content = td.renderContents().replace('\r', ' ') \ - .split('
') + content = td.encode_contents().decode().replace('\r', ' ').split('
') class_ = td['class'] - if class_ == 'sroDtEvent': + if 'sroDtEvent' in class_: data = '%s %s' % (content[0].strip(), content[1].strip()) - local = '/'.join(self._text(content[2]).rsplit(' / ', 1)).upper() - elif class_ == 'sroLbEvent': - situacao = html_parser.unescape(self._text(content[0])) - detalhes = html_parser.unescape(self._text(content[1])) + local = '/'.join(self._text(content[2]).rsplit(' / ', 1)).upper().rstrip('/') + elif 'sroLbEvent' in class_: + situacao = unescape(self._text(content[0])) + detalhes = unescape(self._text(content[1])) if detalhes: detalhes = u'%s %s' % (situacao, detalhes) status.append(Status(data=data, local=local, diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..f3c7e8e --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1 @@ +nose diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 5e0d12c..0000000 --- a/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -BeautifulSoup==3.2.1 -argparse==1.2.1 -beautifulsoup4==4.3.2 -lxml==2.3.5 -mockito==0.5.1 -requests==2.18.1 -wsgiref==0.1.2 -zeep==1.6.0 diff --git a/setup.py b/setup.py index dfbab09..81159e0 100755 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='packtrack', - version='1.6', + version='2.0', packages=['packtrack'], author='Ale Borba', author_email='ale.borba@codingforchange.com', @@ -13,9 +13,8 @@ url='https://github.com/aleborba/packtrack', long_description='API Python para obter informacoes de encomendas. Para mais detalhes veja a documentacao no Github: https://github.com/aleborba/packtrack/blob/master/README.textile', install_requires=[ - 'BeautifulSoup >= 3.1.0', - 'requests >= 0.14.2', - 'beautifulsoup4 >= 4.3.2', + 'requests', + 'beautifulsoup4 >= 4.9.3', 'lxml >= 2.3.5', 'zeep >= 1.6.0', ], diff --git a/tests/correios_api_test.py b/tests/correios_api_test.py index 6777ec3..222ef13 100644 --- a/tests/correios_api_test.py +++ b/tests/correios_api_test.py @@ -1,29 +1,25 @@ -import unittest - -from mock import Mock -from mockito import when +from unittest import TestCase +from unittest.mock import Mock from packtrack import Correios -class CorreiosTest(unittest.TestCase): +class CorreiosTest(TestCase): def test_should_use_repository_to_get_encomenda(self): encomenda_repository_mock = Mock() - when(encomenda_repository_mock).get('123', auth=None) \ - .thenReturn('encomenda123') - + encomenda_repository_mock.get.return_value = 'encomenda123' Correios._backends[None] = encomenda_repository_mock assert Correios.track('123') == 'encomenda123' + encomenda_repository_mock.get.assert_called_with('123', auth=None) def test_service_should_receive_auth(self): auth = ('mi', 'mimi') encomenda_repository_mock = Mock() - when(encomenda_repository_mock).get('123', auth=auth) \ - .thenReturn('encomenda123') - + encomenda_repository_mock.get.return_value = 'encomenda123' Correios._backends['service'] = encomenda_repository_mock assert Correios.track( '123', backend='service', auth=auth) == 'encomenda123' + encomenda_repository_mock.get.assert_called_with('123', auth=auth) diff --git a/tests/correios_test.py b/tests/correios_test.py index d24712e..65cc364 100644 --- a/tests/correios_test.py +++ b/tests/correios_test.py @@ -1,23 +1,23 @@ import unittest - -from mock import Mock -from mockito import * +from unittest.mock import Mock from packtrack.correios import Encomenda, Status, EncomendaRepository + class EncomendaRepositoryTest(unittest.TestCase): def test_should_get_encomenda_by_numero(self): encomenda_123 = Status(data='2009-01-28 17:49:00') - + correios_website_scraper_mock = Mock() - when(correios_website_scraper_mock).get_encomenda_info('123', auth=None).thenReturn(encomenda_123) - + correios_website_scraper_mock.get_encomenda_info.return_value = encomenda_123 + repository = EncomendaRepository() repository.correios_website_scraper = correios_website_scraper_mock encomenda = repository.get('123') assert encomenda assert encomenda.data == '2009-01-28 17:49:00' + correios_website_scraper_mock.get_encomenda_info.assert_called_with('123', auth=None) def test_select_default_backend(self): repository = EncomendaRepository() diff --git a/tests/scraping_test.py b/tests/scraping_test.py index 547c605..0cb1dc7 100644 --- a/tests/scraping_test.py +++ b/tests/scraping_test.py @@ -1,8 +1,7 @@ # encoding: UTF-8 import os import unittest - -import mock +from unittest.mock import Mock from packtrack.scraping import CorreiosWebsiteScraper from packtrack.dhl_gm import DhlGmTracker @@ -17,14 +16,13 @@ def _assert_status(self, status, data, local, situacao, detalhes): self.assertEqual(detalhes, status.detalhes) def test_should_get_data_from_correios_website(self): - example_file = open('%s/tests/correios_website/exemplo_rastreamento_correios1.html' % os.getcwd()) - sample_html = example_file.read() - example_file.close() + with open(f'{os.getcwd()}/tests/correios_website/exemplo_rastreamento_correios1.html', 'r', encoding='latin-1') as f: + sample_html = f.read() - http_client_mock = mock.Mock() - response_mock = mock.Mock() + http_client_mock = Mock() + response_mock = Mock() http_client_mock.post.return_value = response_mock - response_mock.content = sample_html + response_mock.text = sample_html correios_website_scraper = CorreiosWebsiteScraper(http_client_mock) numero = 'PJ859656941BR' @@ -47,10 +45,10 @@ class CorreiosTimeoutTest(unittest.TestCase): def test_timeout_undefined(self): - http_client_mock = mock.Mock() - response_mock = mock.Mock() + http_client_mock = Mock() + response_mock = Mock() http_client_mock.post.return_value = response_mock - response_mock.content = '' + response_mock.text = '' TIMEOUT = 3 scraper = CorreiosWebsiteScraper(http_client_mock, timeout=TIMEOUT) scraper.get_encomenda_info('ES446391025BR')