Plain is headed towards 1.0! Subscribe for development updates →

  1from functools import cached_property
  2
  3from plain.auth.views import AuthViewMixin
  4from plain.htmx.views import HTMXViewMixin
  5from plain.http import JsonResponse, Response, ResponseRedirect
  6from plain.runtime import settings
  7from plain.views import TemplateView
  8
  9from .core import Observer
 10from .models import Trace
 11
 12
 13class ObserverTracesView(AuthViewMixin, HTMXViewMixin, TemplateView):
 14    template_name = "observer/traces.html"
 15    admin_required = True
 16
 17    @cached_property
 18    def observer(self):
 19        """Get the Observer instance for this request."""
 20        return Observer(self.request)
 21
 22    def check_auth(self):
 23        # Allow the view if we're in DEBUG
 24        if settings.DEBUG:
 25            return
 26
 27        super().check_auth()
 28
 29    def get_response(self):
 30        response = super().get_response()
 31        # So we can load it in the toolbar
 32        response.headers["X-Frame-Options"] = "SAMEORIGIN"
 33        return response
 34
 35    def get_template_context(self):
 36        context = super().get_template_context()
 37        context["observer"] = self.observer
 38        context["traces"] = Trace.objects.all()
 39        if trace_id := self.request.query_params.get("trace_id"):
 40            context["trace"] = Trace.objects.filter(id=trace_id).first()
 41        else:
 42            context["trace"] = context["traces"].first()
 43        return context
 44
 45    def get(self):
 46        # Check if JSON format is requested
 47        if self.request.query_params.get("format") == "json":
 48            if trace_id := self.request.query_params.get("trace_id"):
 49                if trace := Trace.objects.filter(id=trace_id).first():
 50                    return JsonResponse(trace.as_dict())
 51            return JsonResponse({"error": "Trace not found"}, status=404)
 52
 53        return super().get()
 54
 55    def htmx_post_enable_summary(self):
 56        """Enable summary mode via HTMX."""
 57        response = Response(status_code=204)
 58        response.headers["HX-Refresh"] = "true"
 59        self.observer.enable_summary_mode(response)
 60        return response
 61
 62    def htmx_post_enable_persist(self):
 63        """Enable full persist mode via HTMX."""
 64        response = Response(status_code=204)
 65        response.headers["HX-Refresh"] = "true"
 66        self.observer.enable_persist_mode(response)
 67        return response
 68
 69    def htmx_post_disable(self):
 70        """Disable observer via HTMX."""
 71        response = Response(status_code=204)
 72        response.headers["HX-Refresh"] = "true"
 73        self.observer.disable(response)
 74        return response
 75
 76    def htmx_delete_traces(self):
 77        """Clear all traces via HTMX DELETE."""
 78        Trace.objects.all().delete()
 79        response = Response(status_code=204)
 80        response.headers["HX-Refresh"] = "true"
 81        return response
 82
 83    def htmx_delete_trace(self):
 84        """Delete a specific trace via HTMX DELETE."""
 85        trace_id = self.request.query_params.get("trace_id")
 86        Trace.objects.get(id=trace_id).delete()
 87        response = Response(status_code=204)
 88        response.headers["HX-Refresh"] = "true"
 89        return response
 90
 91    def post(self):
 92        """A standard, non-htmx post used by the button html (where htmx may not be available)."""
 93
 94        observe_action = self.request.data["observe_action"]
 95
 96        response = ResponseRedirect(self.request.data.get("redirect_url", "."))
 97
 98        if observe_action == "summary":
 99            self.observer.enable_summary_mode(response)  # Default to summary mode
100        elif observe_action == "persist":
101            self.observer.enable_persist_mode(response)
102        elif observe_action == "disable":
103            self.observer.disable(response)
104
105        return response