1# Copyright 2013-2018 The Distro Tracker Developers 

2# See the COPYRIGHT file at the top-level directory of this distribution and 

3# at https://deb.li/DTAuthors 

4# 

5# This file is part of Distro Tracker. It is subject to the license terms 

6# in the LICENSE file found in the top-level directory of this 

7# distribution and at https://deb.li/DTLicense. No part of Distro Tracker, 

8# including this file, may be copied, modified, propagated, or distributed 

9# except according to the terms contained in the LICENSE file. 

10"""Implements the RSS news feed.""" 

11 

12import re 

13from itertools import chain 

14 

15from django.conf import settings 

16from django.contrib.syndication.views import Feed 

17from django.http import Http404 

18 

19from distro_tracker.core.models import ( 

20 ActionItem, 

21 News, 

22 NewsRenderer, 

23 get_web_package 

24) 

25 

26 

27def filter_control_chars(method): 

28 """Filter out undesirable control characters.""" 

29 # We have to filter out control chars otherwise the FeedGenerator 

30 # raises UnserializableContentError (see django/utils/xmlutils.py) 

31 def wrapped(self, obj): 

32 result = method(self, obj) 

33 return re.sub(r'[\x00-\x08\x0B-\x0C\x0E-\x1F]', '', result) 

34 return wrapped 

35 

36 

37class PackageNewsFeed(Feed): 

38 _DEFAULT_LIMIT = 30 

39 

40 def get_object(self, request, package_name): 

41 package = get_web_package(package_name) 

42 if package is None: 

43 raise Http404 

44 

45 return package 

46 

47 @filter_control_chars 

48 def title(self, obj): 

49 return "{vendor} package news for {pkg}".format( 

50 vendor=settings.DISTRO_TRACKER_VENDOR_NAME, 

51 pkg=obj.name) 

52 

53 @filter_control_chars 

54 def link(self, obj): 

55 return obj.get_absolute_url() 

56 

57 @filter_control_chars 

58 def description(self, obj): 

59 return ( 

60 "Latest developer's news for {vendor} source package {pkg}" 

61 .format(vendor=settings.DISTRO_TRACKER_VENDOR_NAME, pkg=obj.name) 

62 ) 

63 

64 def items(self, obj): 

65 item_limit = getattr(settings, 'DISTRO_TRACKER_RSS_ITEM_LIMIT', 

66 self._DEFAULT_LIMIT) 

67 

68 news = obj.news_set.all() 

69 action_items = obj.action_items.all() 

70 

71 def item_key(item): 

72 if isinstance(item, ActionItem): 

73 return item.last_updated_timestamp 

74 elif isinstance(item, News): 74 ↛ exitline 74 didn't return from function 'item_key', because the condition on line 74 was never false

75 return item.datetime_created 

76 

77 all_items = chain(news, action_items) 

78 return sorted(all_items, key=item_key, reverse=True)[:item_limit] 

79 

80 @filter_control_chars 

81 def item_title(self, item): 

82 if isinstance(item, News): 

83 return item.title 

84 elif isinstance(item, ActionItem): 84 ↛ exitline 84 didn't return from function 'item_title', because the condition on line 84 was never false

85 return item.short_description 

86 

87 @filter_control_chars 

88 def item_description(self, item): 

89 if isinstance(item, News): 

90 renderer_class = NewsRenderer.get_renderer_for_content_type( 

91 item.content_type) 

92 if renderer_class is None: 92 ↛ 93line 92 didn't jump to line 93, because the condition on line 92 was never true

93 renderer_class = NewsRenderer.get_renderer_for_content_type( 

94 'text/plain') 

95 renderer = renderer_class(item) 

96 

97 return renderer.render_to_string() 

98 elif isinstance(item, ActionItem): 98 ↛ exitline 98 didn't return from function 'item_description', because the condition on line 98 was never false

99 return item.full_description 

100 

101 def item_pubdate(self, item): 

102 if isinstance(item, ActionItem): 

103 return item.last_updated_timestamp 

104 elif isinstance(item, News): 104 ↛ exitline 104 didn't return from function 'item_pubdate', because the condition on line 104 was never false

105 return item.datetime_created