1from functools import cached_property
2
3from plain.admin.cards import Card
4from plain.admin.views import (
5 AdminModelDetailView,
6 AdminModelListView,
7 AdminModelUpdateView,
8 AdminViewset,
9 register_viewset,
10)
11from plain.postgres import QuerySet
12from plain.postgres.forms import ModelForm
13from plain.preflight import PreflightResult
14
15from .models import Flag, FlagResult
16
17
18class UnusedFlagsCard(Card):
19 title = "Unused Flags"
20
21 @cached_property
22 def flag_errors(self) -> list[PreflightResult]:
23 return Flag.preflight()
24
25 def get_metric(self) -> int:
26 return len(self.flag_errors)
27
28 def get_text(self) -> str:
29 return "\n".join(str(e.fix) for e in self.flag_errors)
30
31
32@register_viewset
33class FlagAdmin(AdminViewset):
34 class ListView(AdminModelListView):
35 model = Flag
36 description = "Toggle features on/off without deploying code."
37 fields = ["name", "enabled", "created_at__date", "used_at__date"]
38 search_fields = ["name", "description"]
39 cards = [UnusedFlagsCard]
40 nav_section = "Feature flags"
41 nav_icon = "flag"
42
43 class DetailView(AdminModelDetailView):
44 model = Flag
45
46
47class FlagResultForm(ModelForm):
48 class Meta:
49 model = FlagResult
50 fields = ["key", "value"]
51
52
53@register_viewset
54class FlagResultAdmin(AdminViewset):
55 class ListView(AdminModelListView):
56 model = FlagResult
57 title = "Flag results"
58 description = "Cached flag evaluations for specific keys (users, teams, etc)."
59 fields = [
60 "flag",
61 "key",
62 "value",
63 "created_at__date",
64 "updated_at__date",
65 ]
66 search_fields = ["flag__name", "key"]
67 nav_section = "Feature flags"
68 nav_icon = "check2-square"
69
70 def get_initial_queryset(self) -> QuerySet:
71 return self.model.query.all().select_related("flag")
72
73 class DetailView(AdminModelDetailView):
74 model = FlagResult
75 title = "Flag result"
76
77 class UpdateView(AdminModelUpdateView):
78 model = FlagResult
79 title = "Update flag result"
80 form_class = FlagResultForm
81 template_name = "admin/plainflags/flagresult_form.html"