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
« prev ^ index » next coverage.py v7.6.7, created at 2025-08-14 10:31 +0000
1from collections import defaultdict
2from functools import cache
4from django import template
5from django.apps import apps
7register = template.Library()
10@cache
11def collect_menus():
12 categories = defaultdict(list)
13 main_menu = []
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}
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
32 categories[item["category"]]["items"].append(item)
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"]))
46@register.inclusion_tag("menu/menu.html", takes_context=True)
47def render_main_menu(context):
48 """Render the main menu in this place.
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
56 main_menu = collect_menus()
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
69 return {"menu": main_menu, "request": context.get("request")}
72@cache
73def collect_usermenu():
74 sections = defaultdict(list)
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}
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)
90 sections = sections.values()
91 for section in sections:
92 section["items"] = sorted(
93 section["items"], key=lambda x: (x["key"], x["title"])
94 )
96 return sorted(sections, key=lambda x: (x["key"], x["name"]))
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}
104 request = context.get("request")
105 path = request.path
107 user_menu = collect_usermenu()
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 )
118 # Highlight active item
119 for item in section["submenu"]:
120 item["active"] = item["url"] == path
122 # Hide sections with no shown items
123 user_menu = filter(lambda section: len(section["submenu"]) > 0, user_menu)
125 return {"menu": user_menu, "request": request, "authenticated": True}