Coverage for amazonorders/entity/item.py: 98.48%

66 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-25 22:50 +0000

1import logging 

2from datetime import datetime, date 

3from typing import Optional 

4 

5from bs4 import Tag 

6 

7from amazonorders.entity.parsable import Parsable 

8from amazonorders.entity.seller import Seller 

9from amazonorders.session import BASE_URL 

10 

11__author__ = "Alex Laird" 

12__copyright__ = "Copyright 2024, Alex Laird" 

13__version__ = "1.0.5" 

14 

15logger = logging.getLogger(__name__) 

16 

17 

18class Item(Parsable): 

19 """ 

20 An Item in an Amazon `~amazonorders.entity.order.Order`. 

21 """ 

22 

23 def __init__(self, 

24 parsed: Tag) -> None: 

25 super().__init__(parsed) 

26 

27 #: The Item title. 

28 self.title: str = self.safe_parse(self._parse_title) 

29 #: The Item link. 

30 self.link: str = self.safe_parse(self._parse_link) 

31 #: The Item price. 

32 self.price: Optional[float] = self.safe_parse(self._parse_price) 

33 #: The Item Seller. 

34 self.seller: Optional[Seller] = self.safe_parse(self._parse_seller) 

35 #: The Item condition. 

36 self.condition: Optional[str] = self.safe_parse(self._parse_condition) 

37 #: The Item return eligible date. 

38 self.return_eligible_date: Optional[date] = self.safe_parse( 

39 self._parse_return_eligible_date) 

40 #: The Item image URL. 

41 self.image_link = self.safe_parse(self._parse_image_link) 

42 #: The Item quantity. 

43 self.quantity = self.safe_parse(self._parse_quantity) 

44 

45 def __repr__(self) -> str: 

46 return "<Item: \"{}\">".format(self.title) 

47 

48 def __str__(self) -> str: # pragma: no cover 

49 return "Item: {}".format(self.title) 

50 

51 def __lt__(self, other): 

52 return self.title < other.title 

53 

54 def _parse_title(self) -> str: 

55 tag = self.parsed.find("a") 

56 return tag.text.strip() 

57 

58 def _parse_link(self) -> str: 

59 tag = self.parsed.find("a") 

60 return "{}{}".format(BASE_URL, tag.attrs["href"]) 

61 

62 def _parse_price(self) -> Optional[float]: 

63 for tag in self.parsed.find_all("div"): 

64 if tag.text.strip().startswith("$"): 

65 return float(tag.text.strip().replace("$", "")) 

66 

67 return None 

68 

69 def _parse_seller(self) -> Optional[Seller]: 

70 for tag in self.parsed.find_all("div"): 

71 if "Sold by:" in tag.text: 

72 return Seller(tag) 

73 

74 return None 

75 

76 def _parse_condition(self) -> Optional[str]: 

77 for tag in self.parsed.find_all("div"): 

78 if "Condition:" in tag.text: 

79 return tag.text.split("Condition:")[1].strip() 

80 

81 return None 

82 

83 def _parse_return_eligible_date(self) -> Optional[date]: 

84 for tag in self.parsed.find_all("div"): 

85 if "Return" in tag.text: 

86 split_str = "through " 

87 if "closed on " in tag.text: 

88 split_str = "closed on " 

89 clean_str = tag.text.strip() 

90 if split_str in clean_str: 

91 date_str = clean_str.split(split_str)[1] 

92 return datetime.strptime(date_str, "%b %d, %Y").date() 

93 

94 return None 

95 

96 def _parse_image_link(self) -> Optional[str]: 

97 img = self.parsed.find_previous_sibling().find("img") 

98 if img: 

99 return img.attrs["src"] 

100 else: 

101 return None 

102 

103 def _parse_quantity(self) -> Optional[int]: 

104 tag = self.parsed.find_previous_sibling().find("span", {"class": "item-view-qty"}) 

105 if tag: 

106 return int(tag.text.strip()) 

107 else: 

108 return None