Coverage for webapp / template_utils.py: 97%

67 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2025-12-29 22:06 +0000

1# Core 

2import hashlib 

3import os 

4from dateutil import parser 

5from emoji import replace_emoji 

6 

7 

8# generator functions for templates 

9def generate_slug(path): 

10 """ 

11 Generate a slug for each page 

12 """ 

13 if path.endswith( 

14 ( 

15 "/snaps", 

16 "/listing", 

17 "/releases", 

18 "/metrics", 

19 "/publicise", 

20 "/publicise/badges", 

21 "/publicise/cards", 

22 "/settings", 

23 "/account/details", 

24 ) 

25 ): 

26 return "account" 

27 

28 if path == "/" or path.startswith("/first-snap"): 

29 return "home" 

30 

31 if path.startswith("/build"): 

32 return "build" 

33 

34 if path.startswith("/blog"): 

35 return "blog" 

36 

37 if path.startswith("/iot"): 

38 return "iot" 

39 

40 if path.startswith("/docs/snap-tutorials"): 

41 return "tutorials" 

42 

43 if path.startswith("/docs"): 

44 return "docs" 

45 

46 return "store" 

47 

48 

49# template filters 

50def contains(arr, contents): 

51 """ 

52 Template helper for detecting if an array contains an item 

53 """ 

54 

55 return contents in arr 

56 

57 

58def join(arr, separator=""): 

59 """ 

60 Template helper for joining array items into a string, using a separator 

61 """ 

62 

63 return separator.join(arr) 

64 

65 

66def static_url(filename): 

67 """ 

68 Template function for generating URLs to static assets: 

69 Given the path for a static file, output a url path 

70 with a hex hash as a query string for versioning 

71 """ 

72 

73 filepath = os.path.join("static", filename) 

74 url = "/" + filepath 

75 

76 if not os.path.isfile(filepath): 

77 # Could not find static file 

78 return url 

79 

80 # Use MD5 as we care about speed a lot 

81 # and not security in this case 

82 file_hash = hashlib.md5() 

83 with open(filepath, "rb") as file_contents: 

84 for chunk in iter(lambda: file_contents.read(4096), b""): 

85 file_hash.update(chunk) 

86 

87 return url + "?v=" + file_hash.hexdigest()[:7] 

88 

89 

90def install_snippet( 

91 package_name, default_track, lowest_risk_available, confinement 

92): 

93 """ 

94 Template function that returns the snippet value to 

95 install a snap to be used in distro pages and/or snap 

96 detail pages 

97 """ 

98 

99 snippet_value = "sudo snap install " + package_name 

100 

101 if lowest_risk_available != "stable": 

102 snippet_value += f" --{lowest_risk_available}" 

103 

104 if confinement == "classic": 

105 snippet_value += " --classic" 

106 

107 return snippet_value 

108 

109 

110def format_number(number: int): 

111 """ 

112 Template function that transforms a int into a string 

113 with a comma between every thousands 

114 """ 

115 return "{:,}".format(number) 

116 

117 

118def format_display_name(display_name): 

119 """Template function that formats the displayed name 

120 primarily to remove emoji 

121 """ 

122 return replace_emoji(display_name, replace="") 

123 

124 

125def display_name(display_name, username): 

126 """Template function that returns the displayed name if the username 

127 is the same, or the dispayed name and the username if differents 

128 """ 

129 display_name = format_display_name(display_name) 

130 if display_name.lower() == username.lower(): 

131 return display_name 

132 else: 

133 return f"{display_name} ({username})" 

134 

135 

136def format_date(timestamp, format): 

137 """Template function that returns a formatted date 

138 based on the given timestamp 

139 """ 

140 datestring = parser.parse(timestamp) 

141 

142 return datestring.strftime(format) 

143 

144 

145def format_member_role(role): 

146 """Template function that returns the 

147 correct label for a members role 

148 """ 

149 roles = { 

150 "admin": "admin", 

151 "review": "reviewer", 

152 "view": "viewer", 

153 "access": "publisher", 

154 } 

155 

156 return roles[role] 

157 

158 

159def format_link(url): 

160 """ 

161 Template function that removes protocol, path and query string from links 

162 """ 

163 url_parts = url.split(":") 

164 

165 if url_parts[0] == "mailto": 

166 return url_parts[1] 

167 

168 if url_parts[0] == "http" or url_parts[0] == "https": 

169 url_parts_no_slashes = url_parts[1].split("//")[1] 

170 url_parts_no_query = url_parts_no_slashes.split("?")[0] 

171 url_parts_no_path = url_parts_no_query.split("/")[0] 

172 

173 if url_parts_no_path in [ 

174 "github.com", 

175 "gitlab.com", 

176 "bitbucket.org", 

177 "launchpad.net", 

178 "sourceforge.net", 

179 ]: 

180 return url_parts_no_query 

181 

182 return url_parts_no_path