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
« prev ^ index » next coverage.py v7.3.0, created at 2024-12-08 12:26 +0000
1import datetime
2import pathlib
3import re
6r_filename = re.compile(
7 r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})(-(?P<slug>.*))?')
10class Image:
11 """
12 A website image.
13 """
15 def __init__(self, path: str | pathlib.Path, entry=None):
16 self._path = pathlib.Path(path)
17 self._entry = entry
19 @property
20 def path(self) -> pathlib.Path:
21 """
22 Image as a `pathlib.Path` object.
23 """
24 return self._path
26 @property
27 def filename(self):
28 """
29 Name of the file, ex `test.jpg`
30 """
31 return self.path.name
33 @property
34 def date(self) -> datetime.datetime:
35 """
36 Date, according to the image file's YYY-MM-DD date slug.
37 """
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}')
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')
54 @property
55 def slug(self):
56 """
57 The portion of the filename without the extension or the date slug.
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')
65 # otherwise just return the stem
66 return self.path.stem
68 @property
69 def title(self):
70 """
71 Human readable name for the image, based on the date slug.
73 For example, `test-image.jpg`, becomes `Test Image`
74 """
75 return self.slug.replace('-', ' ').title()
77 @property
78 def href(self):
79 """
80 The `href` html value that points to the image.
82 Can be used in templates like so:
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}'
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
99 @property
100 def entry(self):
101 """
102 The entry where the image is referenced.
103 """
104 return self._entry
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.
111 Requires a list of entries so it can associate the entry where it
112 is referenced.
114 ```python
115 images = src.load_images()
116 ```
117 """
119 images = []
121 def is_image(p):
122 return p.suffix.lower() in (
123 '.jpg',
124 '.jpeg',
125 '.png',
126 )
128 images_dir = pathlib.Path(images_dir)
129 image_files = filter(is_image, images_dir.glob('**/*.*'))
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
137 # build the list of images
138 for path in image_files:
139 images.append(Image(path, ref_map.get(str(path))))
141 # finally, sort them by name
142 return sorted(images, key=lambda i: i.path.name, reverse=True)