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"