Coverage for website/thaliawebsite/templatetags/menu.py: 99.17%

74 statements  

« prev     ^ index     » next       coverage.py v7.6.7, created at 2025-08-14 10:31 +0000

1from collections import defaultdict 

2from functools import cache 

3 

4from django import template 

5from django.apps import apps 

6 

7register = template.Library() 

8 

9 

10@cache 

11def collect_menus(): 

12 categories = defaultdict(list) 

13 main_menu = [] 

14 

15 for app in apps.get_app_configs(): 

16 if hasattr(app, "menu_items"): 

17 menu = app.menu_items() 

18 if "categories" in menu: 

19 for category in menu["categories"]: 

20 assert "key" in category 

21 if category["name"] not in categories: 

22 categories[category["name"]] = {"items": [], **category} 

23 

24 for item in menu["items"]: 

25 assert "url" in item, item 

26 if "category" not in item: 

27 # Main item 

28 main_menu.append(item) 

29 else: 

30 assert item["category"] in categories 

31 

32 categories[item["category"]]["items"].append(item) 

33 

34 for category in categories.values(): 

35 main_menu.append( 

36 { 

37 "submenu": sorted( 

38 category["items"], key=lambda x: (x["key"], x["title"]) 

39 ), 

40 **category, 

41 } 

42 ) 

43 return sorted(main_menu, key=lambda x: (x["key"], x["title"])) 

44 

45 

46@register.inclusion_tag("menu/menu.html", takes_context=True) 

47def render_main_menu(context): 

48 """Render the main menu in this place. 

49 

50 Accounts for logged-in status and locale. 

51 """ 

52 path = None 

53 if "request" in context: 53 ↛ 56line 53 didn't jump to line 56 because the condition on line 53 was always true

54 path = context.get("request").path 

55 

56 main_menu = collect_menus() 

57 

58 for item in main_menu: 

59 active = "url" in item and item["url"] == path 

60 if not active and "submenu" in item: 

61 for subitem in item["submenu"]: 

62 if subitem["url"] == path: 

63 subitem["active"] = True 

64 active = True 

65 else: 

66 subitem["active"] = False 

67 item["active"] = active 

68 

69 return {"menu": main_menu, "request": context.get("request")} 

70 

71 

72@cache 

73def collect_usermenu(): 

74 sections = defaultdict(list) 

75 

76 for app in apps.get_app_configs(): 

77 if hasattr(app, "user_menu_items"): 

78 menu = app.user_menu_items() 

79 if "sections" in menu: 

80 for section in menu["sections"]: 

81 assert "key" in section 

82 if section["name"] not in sections: 

83 sections[section["name"]] = {"items": [], **section} 

84 

85 for item in menu["items"]: 

86 assert "url" in item, item 

87 assert item["section"] in sections 

88 sections[item["section"]]["items"].append(item) 

89 

90 sections = sections.values() 

91 for section in sections: 

92 section["items"] = sorted( 

93 section["items"], key=lambda x: (x["key"], x["title"]) 

94 ) 

95 

96 return sorted(sections, key=lambda x: (x["key"], x["name"])) 

97 

98 

99@register.inclusion_tag("menu/usermenu.html", takes_context=True) 

100def render_user_menu(context): 

101 if "request" not in context or not context["request"].user.is_authenticated: 

102 return {"authenticated": False} 

103 

104 request = context.get("request") 

105 path = request.path 

106 

107 user_menu = collect_usermenu() 

108 

109 for section in user_menu: 

110 # Create filtered copy of items that only shows 'enabled' items 

111 section["submenu"] = list( 

112 filter( 

113 lambda item: "show" not in item or item["show"](request), 

114 section["items"], 

115 ) 

116 ) 

117 

118 # Highlight active item 

119 for item in section["submenu"]: 

120 item["active"] = item["url"] == path 

121 

122 # Hide sections with no shown items 

123 user_menu = filter(lambda section: len(section["submenu"]) > 0, user_menu) 

124 

125 return {"menu": user_menu, "request": request, "authenticated": True}