Coverage for src/models/feed.py: 36%

39 statements  

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

1import collections 

2 

3from src.template import render_template 

4from src import xml 

5 

6 

7Item = collections.namedtuple('Item', [ 

8 'title', 

9 'timestamp', 

10 'path', 

11 'image', 

12]) 

13 

14 

15class Feed: 

16 """ 

17 Website RSS feed. 

18 

19 An atom feed that you can build from site information and write 

20 locally as a valid atom RSS feed. 

21 """ 

22 

23 filename = 'feed.xml' 

24 

25 def __init__(self, site=None, items: list[Item] = []): # noqa: E501 

26 """ 

27 Build a feed object. 

28 

29 Takes a list of `Item` objects, which is just this named 

30 tuple: 

31 

32 ```python 

33 Item = collections.namedtuple('Item', [ 

34 'title', 

35 'timestamp', 

36 'path', # ex. 2020-01-01.html 

37 'image', # ex. 2021-01-01.jpg 

38 ]) 

39 ``` 

40 """ 

41 self.site = site 

42 self.items = items 

43 

44 def render(self): 

45 content = render_template('feed.xml.j2', context={ 

46 'filename': self.filename, 

47 'site': self.site, 

48 'items': self.items, 

49 }) 

50 return xml.prettify(content) 

51 

52 def write(self): 

53 with open(f'./www/{self.filename}', 'w') as f: 

54 f.write(self.render()) 

55 

56 def __repr__(self): 

57 return f'<Feed {self.filename}>' 

58 

59 

60def load_feed(site, entries=[], images=[]) -> Feed: # noqa: E501 

61 """ 

62 Load an RSS feed object. 

63 

64 ```python 

65 feed = load_feed(site) 

66 ``` 

67 """ 

68 items = [] 

69 

70 def convert_timestamp(date): 

71 slug = date.strftime("%Y-%m-%d") 

72 return f'{slug}T00:00:00+00:00' 

73 

74 # add all journal entries 

75 for entry in entries: 

76 kwargs = {} 

77 kwargs['title'] = entry.title 

78 kwargs['path'] = entry.filename 

79 

80 if entry.banner: 

81 kwargs['image'] = f'images/{entry.banner}' 

82 else: 

83 kwargs['image'] = None 

84 

85 kwargs['timestamp'] = convert_timestamp(entry.date) 

86 items.append(Item(**kwargs)) 

87 

88 # add all other images that aren't a banner 

89 banners = [e.banner for e in entries if e.banner] 

90 for image in images: 

91 if image.filename in banners: 

92 continue 

93 

94 kwargs = { 

95 'title': image.title, 

96 'path': f'images/{image.filename}', 

97 'image': f'images/{image.filename}', 

98 'timestamp': convert_timestamp(image.date), 

99 } 

100 items.append(Item(**kwargs)) 

101 

102 # sort by descending timestamp 

103 items = sorted(items, key=lambda i: i.timestamp, reverse=True) 

104 

105 return Feed(site=site, items=items)