Coverage for src/models/image.py: 76%

54 statements  

« prev     ^ index     » next       coverage.py v7.3.0, created at 2024-12-08 12:26 +0000

1import datetime 

2import pathlib 

3import re 

4 

5 

6r_filename = re.compile( 

7 r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})(-(?P<slug>.*))?') 

8 

9 

10class Image: 

11 """ 

12 A website image. 

13 """ 

14 

15 def __init__(self, path: str | pathlib.Path, entry=None): 

16 self._path = pathlib.Path(path) 

17 self._entry = entry 

18 

19 @property 

20 def path(self) -> pathlib.Path: 

21 """ 

22 Image as a `pathlib.Path` object. 

23 """ 

24 return self._path 

25 

26 @property 

27 def filename(self): 

28 """ 

29 Name of the file, ex `test.jpg` 

30 """ 

31 return self.path.name 

32 

33 @property 

34 def date(self) -> datetime.datetime: 

35 """ 

36 Date, according to the image file's YYY-MM-DD date slug. 

37 """ 

38 

39 if match := r_filename.search(self.path.stem): 

40 return datetime.datetime( 

41 year=int(match.group('year')), 

42 month=int(match.group('month')), 

43 day=int(match.group('day')), 

44 ) 

45 raise ValueError(f'could not parse date from {self.filename}') 

46 

47 @property 

48 def date_slug(self): 

49 """ 

50 Parses the YYYY-MM-DD date slug from the file name. 

51 """ 

52 return self.date.strftime('%Y-%m-%d') 

53 

54 @property 

55 def slug(self): 

56 """ 

57 The portion of the filename without the extension or the date slug. 

58 

59 If the full filename is `2023-01-01-fish-soup.png`, the slug 

60 would be `fish-soup`. 

61 """ 

62 if match := r_filename.search(self.path.stem): 

63 return match.group('slug') 

64 

65 # otherwise just return the stem 

66 return self.path.stem 

67 

68 @property 

69 def title(self): 

70 """ 

71 Human readable name for the image, based on the date slug. 

72 

73 For example, `test-image.jpg`, becomes `Test Image` 

74 """ 

75 return self.slug.replace('-', ' ').title() 

76 

77 @property 

78 def href(self): 

79 """ 

80 The `href` html value that points to the image. 

81 

82 Can be used in templates like so: 

83 

84 ```html 

85 <a href="{{ img.href }}">...</a> 

86 ``` 

87 """ 

88 www_dir = pathlib.Path('./www') 

89 relpath = self.path.relative_to(www_dir) 

90 return f'./{relpath}' 

91 

92 @property 

93 def is_banner(self): 

94 """ 

95 True if the image lives in the banners directory. 

96 """ 

97 return self.date_slug == self.path.stem 

98 

99 @property 

100 def entry(self): 

101 """ 

102 The entry where the image is referenced. 

103 """ 

104 return self._entry 

105 

106 

107def load_images(entries=[], images_dir='./www/images/') -> list[Image]: 

108 """ 

109 Loads complete set of images for website as a list of `Image` objects. 

110 

111 Requires a list of entries so it can associate the entry where it 

112 is referenced. 

113 

114 ```python 

115 images = src.load_images() 

116 ``` 

117 """ 

118 

119 images = [] 

120 

121 def is_image(p): 

122 return p.suffix.lower() in ( 

123 '.jpg', 

124 '.jpeg', 

125 '.png', 

126 ) 

127 

128 images_dir = pathlib.Path(images_dir) 

129 image_files = filter(is_image, images_dir.glob('**/*.*')) 

130 

131 # build a k/v map of image paths to entries 

132 ref_map = {} 

133 for entry in entries: 

134 for path in entry.extract_links(): 

135 ref_map[str(path)] = entry 

136 

137 # build the list of images 

138 for path in image_files: 

139 images.append(Image(path, ref_map.get(str(path)))) 

140 

141 # finally, sort them by name 

142 return sorted(images, key=lambda i: i.path.name, reverse=True)