diff --git a/package_tracking.py b/package_tracking.py index f7cfae8..2898c47 100644 --- a/package_tracking.py +++ b/package_tracking.py @@ -1,88 +1,106 @@ -import sys - +import argparse import requests + from bs4 import BeautifulSoup -import argparse +from datetime import datetime -class CPostPackage: - """ - Class for storing Ceska posta package information - """ +class CeskaPosta: + def __init__(self, pkg_tracking_number: str): + # base URL + self.base_url = "https://www.postaonline.cz/trackandtrace/-/zasilka/cislo?parcelNumbers=" + + # initialize variables for package + self.pkg_tracking_number = pkg_tracking_number + self.status_pkg = None + self.date_pkg = None + self.psc = None + self.place = None + + # get the values from Ceska Posta and add them to the variables + if pkg_tracking_number is not None: + self.initialize() + else: + raise AttributeError("Package number not provided!") + + def initialize(self): + try: + r = requests.get(self.base_url + self.pkg_tracking_number) + except requests.exceptions.RequestException as e: + raise e + + html_doc = r.content + soup = BeautifulSoup(html_doc, 'html.parser') + + # thrown only if the HTML document contains an error, it is almost always an bad number or not a found number + if soup.select('[class=error]'): + raise ValueError("Neexistující číslo zásilky!") + + # list comprehension for getting the values from html doc + status = [ + element.get_text(strip=True) + for element in soup.select('[class~=datatable2] tr strong') + if element.get_text(strip=True) != '' + ] + + self.date_pkg = status[1] + self.status_pkg = status[2] + self.psc = status[3] + self.place = status[4] + + return self + + def __str__(self): + return f"{self.tracking_number()} | {self.status()} | {self.date()} | {self.place_arrival()} | {self.postal_code()}" + + def __repr__(self): + return f"<{self.__class__.__name__}" \ + f"({self.tracking_number()}, {self.status()}, {self.date()}, {self.place_arrival()}, {self.postal_code()})>" - def __init__(self, tracking_number="DR1234567890E"): - """ - Constructor for CPostPackage class. - """ - # Create some member animals - self.tracking_number = tracking_number - self.tracking_url = "https://www.postaonline.cz/trackandtrace/-/zasilka/cislo?parcelNumbers=" + def date(self) -> str: + """Returns datetime object from provided date.""" + return self.date_pkg + def status(self) -> str: + """Returns package status.""" + return self.status_pkg -def print_package_status(status): + def tracking_number(self) -> str: + """Returns tracking number of package.""" + return self.pkg_tracking_number + + def postal_code(self) -> int: + """Returns postal code.""" + return int(self.psc) + + def place_arrival(self) -> str: + """Returns place of arrival of the package.""" + return self.place + + +def print_package_status(package: CeskaPosta): """ Prints package info in nice format base on last status - :param status: Last package status + :param package: instance of CeskaPosta :return: """ - # Check if the input is list - if type(status) is not list: - raise TypeError - - # Get date and status - package_no = str(status[0]) - package_date = str(status[1]) - package_status = str(status[2]) # Get the line length - current_line = '=' * (len(package_date) + len(package_status) + 7) - no_line = '=' * int((len(current_line) - len(package_no)) / 2) + current_line = '=' * (len(str(package.date())) + len(package.status()) + 7) + no_line = '=' * int((len(current_line) - len(package.tracking_number())) / 2) # If the no. of chars in package no. is odd, align the printed table - if len(no_line) % 2 is 0: - print("{0}{1}{2}".format(no_line, package_no, no_line)) + if len(no_line) % 2 == 0: + print("{0}{1}{2}".format(no_line, package.tracking_number(), no_line)) else: - print("{0}{1}{2}=".format(no_line, package_no, no_line)) + print("{0}{1}{2}=".format(no_line, package.tracking_number(), no_line)) print(current_line) - print("= {0} | {1} =".format(package_date, package_status)) + print("= {0} | {1} =".format(package.date(), package.tracking_number())) print(current_line) -def find_package_status(cpost_pkg): - """ - Gets Ceska posta track and trace page and scrape actual package info based on package no. - :param cpost_pkg: A CPostPackage class with url and track no. - :return: - """ - try: - r = requests.get(cpost_pkg.tracking_url + cpost_pkg.tracking_number) - except requests.exceptions.RequestException as e: - print("{0}".format(e)) - - html_doc = r.content - - soup = BeautifulSoup(html_doc, 'html.parser') - - status = [] - - for elm in soup.select('[class~=datatable2] tr strong'): - # print(elm.get_text(strip=True)) - if elm.get_text(strip=True) is not '': - status.append(elm.get_text(strip=True)) - - try: - print_package_status(status) - except IndexError as e: - print("Neexistujici cislo zasilky!".format(e)) - sys.exit(0) +if __name__ == '__main__': + c = CeskaPosta("DR2350001447U") + print(c) -if __name__ == '__main__': - # Construct the argument parse and parse the arguments - parser = argparse.ArgumentParser() - parser.add_argument('-p', '--package', required=True, help="Package number") - args = parser.parse_args() - - # Create a new delivery class - delivery = CPostPackage(args.package) - find_package_status(delivery) diff --git a/tests/test_package_tracking.py b/tests/test_package_tracking.py index 59c1415..a5328ea 100644 --- a/tests/test_package_tracking.py +++ b/tests/test_package_tracking.py @@ -1,25 +1,30 @@ import pytest +import datetime -from package_tracking import print_package_status, find_package_status +from package_tracking import CeskaPosta -@pytest.mark.parametrize('status', [["DR1234567890E", "1.1.1970", "Nope"], ["EE123456789CZ", "23.11.1969", "Yep"]]) -def test_print_package_status_pass(status): - print_package_status(status) +@pytest.mark.parametrize("package_num", ["DR2350001447U", "DR2350001420U", "DR2350001447U", "DR2350001464U"]) +def test_class(package_num): + assert CeskaPosta(package_num) -@pytest.mark.parametrize('status', [None, "Le String", 25]) -def test_print_package_status_raise_type_err(status): - with pytest.raises(TypeError): - print_package_status(status) +@pytest.mark.parametrize("package_num", ["asdfgw", "NP2350001", "DR2350002447U"]) +def test_errors_class(package_num): + with pytest.raises(ValueError): + assert CeskaPosta(package_num) -@pytest.mark.parametrize('status', [[], ["Uno"], ["Foo", "Bar"]]) -def test_print_package_status_raise_index_err(status): - with pytest.raises(IndexError): - print_package_status(status) +@pytest.mark.parametrize("package_num, expected", + [("DR2350001447U", "8.11.2021"), + ("DR2350001420U", "10.11.2021"), + ("DR2350001447U", "8.11.2021"), + ("DR2350001464U", "8.11.2021")]) +def test_datetime(package_num, expected): + c = CeskaPosta(package_num) + assert c.date() == expected def test_find_package_status_none(): with pytest.raises(AttributeError): - find_package_status(None) + CeskaPosta(None)