Source code for djangoaddicts.pygwalker.views

from __future__ import annotations

import mimetypes

import pandas as pd
import pygwalker as pyg
from django.apps import apps
from django.conf import settings
from django.contrib import messages
from django.db.models import QuerySet
from django.shortcuts import render
from django.views.generic import View
from handyhelpers.views.gui import HandyHelperListView, HandyHelperPaginatedListView

from djangoaddicts.pygwalker.forms import UploadFileForm


[docs] class PygWalkerView(View): """View to create a PyGWalker visualization interface from a Django queryset. See https://github.com/Kanaries/pygwalker for more information on PyGWalker. class parameters: field_list - list of model fields to include; defaults to fields defined in the model queryset - queryset providing data available to visualization template_name - template used when rendering page; defaults to: pygwalker/bs5/pyg.html theme - PyGWalker theme to use for pyg html; defaults to: "media" title - title used on html render example: from djangoaddicts.pygwalker.views import PygWalkerView class MyPygView(PygWalkerView): queryset = Order.objects.all() title = "Order Data Analysis" theme = "light" field_list = ["status__name", "customer", "order_id", "created_at", "updated_at", "products"] """ field_list: list = [] queryset: QuerySet = None template_name: str = "pygwalker/bs5/pygwalker.html" theme: str = getattr(settings, "PYGWALKER_THEME", "media") title: str | None = None def get(self, request): referrer = request.META.get("HTTP_REFERER", "") if "?" in referrer: query_dict = { key: value for key, value in (item.split("=") for item in referrer.split("?")[1].split("&") if item) } else: query_dict = {} self.queryset = self.queryset.filter(**query_dict) if not self.title: self.title = f"{self.queryset.model._meta.model_name.title()} Analysis" pd_data = pd.DataFrame(list(self.queryset.values(*self.field_list))) if pd_data.empty: context = {"no_data": f"no {self.queryset.model.__name__} data found", "title": self.title} else: context = {"pyg": pyg.walk(pd_data, return_html=True, dark=self.theme), "title": self.title} return render(request, self.template_name, context)
[docs] class StaticCsvPygWalkerView(View): """View to create a PyGWalker visualization interface from a statically definied csv file. See https://github.com/Kanaries/pygwalker for more information on PyGWalker. class parameters: csv_file - csv file containing data to visualize template_name - template used when rendering page; defaults to: pygwalker/bs5/pyg.html theme - PyGWalker theme to use for pyg html; defaults to: "media" title - title used on html render example: from djangoaddicts.pygwalker.views import StaticCsvPygWalkerView class MyPygView(StaticCsvPygWalkerView): csv_file = "my_csv_file.csv title = "Order Data Analysis" theme = "light" """ csv_file = None template_name: str = "pygwalker/bs5/pygwalker.html" theme: str = getattr(settings, "PYGWALKER_THEME", "media") title: str = "Data Analysis" def get(self, request): pd_data = pd.read_csv(self.csv_file) context = {"pyg": pyg.walk(pd_data, return_html=True, dark=self.theme), "title": self.title} return render(request, self.template_name, context)
[docs] class DynamicCsvPygWalkerView(View): """View to create a PyGWalker visualization interface from a provided csv file. See https://github.com/Kanaries/pygwalker for more information on PyGWalker. class parameters: template_name - template used when rendering page; defaults to: pygwalker/bs5/pygwalker_dynamic.html theme - PyGWalker theme to use for pyg html; defaults to "media" title - title used on html render """ template_name: str = "pygwalker/bs5/pygwalker_dynamic.html" theme: str = getattr(settings, "PYGWALKER_THEME", "media") title: str = "Upload a csv file" def get(self, request): context = {"form": UploadFileForm(), "title": "Upload a csv file"} return render(request, self.template_name, context) def post(self, request): context = {} form = UploadFileForm(request.POST, request.FILES) if form.is_valid(): csv_file = form.cleaned_data["csv_file"] if mimetypes.guess_type(csv_file.name)[0] not in ["text/csv"]: messages.add_message( request, messages.ERROR, "file provided is not a csv file", extra_tags="alert-danger" ) return render(request, self.template_name, context) pd_data = pd.read_csv(csv_file) context["pyg"] = pyg.walk(pd_data, return_html=True, dark=self.theme) context[ "title" ] = f"""Showing data from <span class="text-secondary">{csv_file.name.split("/")[-1]}</span>""" return render(request, self.template_name, context) else: for error in form.errors: for i in form.errors[error].data: for msg in i.messages: messages.add_message( self.request, messages.ERROR, msg, extra_tags="alert-danger", ) return self.get(request)
[docs] class GenericPygWalkerView(View): """View to create a PyGWalker visualization interface from an app and model passed as kwargs. If query parameters are present, include only filtered data, based on query parameters, in the PyGWalker interface.""" field_list: list = [] queryset: QuerySet = None template_name: str = "pygwalker/bs5/pygwalker.html" theme: str = getattr(settings, "PYGWALKER_THEME", "media")
[docs] def get(self, request, **kwargs): """process GET request""" referrer = request.META.get("HTTP_REFERER", "") if "?" in referrer: query_dict = { key: value for key, value in (item.split("=") for item in referrer.split("?")[1].split("&") if item) } else: query_dict = {} model = apps.get_model(kwargs["app_name"], kwargs["model_name"]) self.queryset = model.objects.filter(**query_dict) title = f"{self.queryset.model._meta.model_name.title()} Analysis" pd_data = pd.DataFrame(list(self.queryset.values(*self.field_list))) if pd_data.empty: context = {"no_data": f"no {self.queryset.model.__name__} data found", "title": title} else: context = {"pyg": pyg.walk(pd_data, return_html=True, dark=self.theme), "title": title} return render(request, self.template_name, context)
class PygWalkerListViewMixin: pygwalker_icon: str = """<i class="fa-solid fa-magnifying-glass-chart"></i>""" pygwalker_title: str = "PyGWalker view" pygwalker_url: str | None = None def get(self, request, *args, **kwargs): if not self.pygwalker_url: self.pygwalker_url = ( f"/pygwalker/generic_pyg/{self.queryset.model._meta.app_label}/{self.queryset.model._meta.model_name}" ) self.extra_controls["pygwalker"] = { "icon": self.pygwalker_icon, "title": self.pygwalker_title, "url": self.pygwalker_url, } return super().get(request, *args, **kwargs)
[docs] class PygWalkerListView(PygWalkerListViewMixin, HandyHelperListView): """extend the HandyHelperListView (from handyhelpers) to add an icon for a PyGWalker visualzation interface. If the list view is filtered, include only filtered data in the PyGWalker interface."""
[docs] class PygWalkerPaginatedListView(PygWalkerListViewMixin, HandyHelperPaginatedListView): """extend the HandyHelperPaginatedListView (from handyhelpers) to add an icon for a PyGWalker visualzation interface. If the list view is filtered, include only filtered data in the PyGWalker interface."""