Source code for salesman.admin.wagtail.panels

from __future__ import annotations

from typing import Any

from django.core.exceptions import FieldDoesNotExist
from django.http import HttpRequest
from django.utils.formats import date_format
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from wagtail.admin.panels import Panel

from salesman.conf import app_settings

from ..utils import format_price


[docs]class ReadOnlyPanel(Panel): """ Read only panel for Wagtail. You can pass in a ``formatter`` function to override value format and/or a ``renderer`` function to override how the value is rendered in html. """ def __init__(self, attr: str, *args: Any, **kwargs: Any) -> None: self.attr = attr self.formatter = kwargs.pop("formatter", None) self.renderer = kwargs.pop("renderer", None) super().__init__(*args, **kwargs)
[docs] def clone_kwargs(self) -> dict[str, Any]: kwargs: dict[str, Any] = super().clone_kwargs() kwargs["attr"] = self.attr kwargs["formatter"] = self.formatter kwargs["renderer"] = self.renderer return kwargs
[docs] def on_model_bound(self) -> None: """ Set field data from model. """ field, heading = None, "" try: field = self.model._meta.get_field(self.attr) heading = getattr(field, "verbose_name", "") except (FieldDoesNotExist, AttributeError): try: field = getattr(self.model, self.attr) heading = getattr(field, "short_description", "") except AttributeError: pass if heading and not self.heading: self.heading: str = heading if field and not self.help_text: self.help_text: str = getattr(field, "help_text", "")
def get_value(self) -> Any: value = getattr(self.instance, self.attr) if callable(value): value = value(self.instance) return value def format_value(self, value: Any) -> Any: if self.formatter and value is not None: value = self.formatter(value, self.instance, self.request) return value def render(self) -> Any: value = self.get_value() return self.format_value(value) def render_as_object(self) -> Any: if self.renderer: return self.renderer(self.get_value(), self.instance, self.request) return format_html( "<fieldset><legend>{}</legend>" '<ul class="fields"><li><div class="field">' '<div style="padding-top: 1.2em;">{}</div>' "</div></li></ul>" "</fieldset>", self.heading, self.render(), ) def render_as_field(self) -> Any: if self.renderer: return self.renderer(self.get_value(), self.instance, self.request) help_html = ( format_html('<p class="help">{}</p>', self.help_text) if self.help_text else "" ) return format_html( '<div class="field">' "<label>{}{}</label>" '<div class="field-content">' '<div style="padding-top: 1.2em;">{}</div>{}' "</div>" "</div>", self.heading, _(":"), self.render(), help_html, )
[docs] class BoundPanel(Panel.BoundPanel): heading: str help_text: str def __init__( self, panel: Panel, **kwargs: Any, ) -> None: super().__init__(panel, **kwargs) self.attr = panel.attr self.formatter = panel.formatter self.renderer = panel.renderer def get_value(self) -> Any: value = getattr(self.instance, self.attr) if callable(value): value = value(self.instance) return value def format_value(self, value: Any) -> Any: if self.formatter and value is not None: value = self.formatter(value, self.instance, self.request) return value def render(self) -> Any: value = self.get_value() return self.format_value(value)
[docs] def render_html(self, context: dict[str, Any]) -> Any: """ New method for rendering the field in Wagtail 4. """ if self.renderer: return self.renderer(self.get_value(), self.instance, self.request) return format_html( "<div>{}</div>", self.render(), )
def render_as_object(self) -> Any: if self.renderer: return self.renderer(self.get_value(), self.instance, self.request) return format_html( "<fieldset><legend>{}</legend>" '<ul class="fields"><li><div class="field">' '<div style="padding-top: 1.2em;">{}</div>' "</div></li></ul>" "</fieldset>", self.heading, self.render(), ) def render_as_field(self) -> Any: if self.renderer: return self.renderer(self.get_value(), self.instance, self.request) help_html = ( format_html('<p class="help">{}</p>', self.help_text) if self.help_text else "" ) return format_html( '<div class="field">' "<label>{}{}</label>" '<div class="field-content">' '<div style="padding-top: 1.2em;">{}</div>{}' "</div>" "</div>", self.heading, _(":"), self.render(), help_html, )
[docs]class OrderDatePanel(ReadOnlyPanel): def format_value(self, value: Any) -> Any: if value: value = date_format(value, format="DATETIME_FORMAT") return value
[docs] class BoundPanel(ReadOnlyPanel.BoundPanel): def format_value(self, value: Any) -> Any: if value: value = date_format(value, format="DATETIME_FORMAT") return value
[docs]class OrderCheckboxPanel(ReadOnlyPanel): def format_value(self, value: Any) -> str: icon, color = ("tick", "#157b57") if value else ("cross", "#cd3238") template = '<span class="icon icon-{}" style="color: {};"></span>' return format_html(template, icon, color)
[docs] class BoundPanel(ReadOnlyPanel.BoundPanel): def format_value(self, value: Any) -> str: icon, color = ("tick", "#157b57") if value else ("cross", "#cd3238") template = '<span class="icon icon-{}" style="color: {};"></span>' return format_html(template, icon, color)
[docs]class OrderItemsPanel(ReadOnlyPanel):
[docs] def classes(self) -> list[str]: return ["salesman-order-items"]
def render_as_field(self) -> str: return self.render() def render_as_object(self) -> str: return self.render() def format_json( self, value: dict[str, Any], obj: Any, request: HttpRequest, ) -> str: return app_settings.SALESMAN_ADMIN_JSON_FORMATTER( value, context={"order_item": True} ) def render(self) -> str: head = f"""<tr> <td>{_('Name')}</td> <td>{_('Code')}</td> <td>{_('Unit price')}</td> <td>{_('Quantity')}</td> <td>{_('Subtotal')}</td> <td>{_('Extra rows')}</td> <td>{_('Total')}</td> <td>{_('Extra')}</td> </tr>""" body = "" for item in self.instance.items.all(): body += f"""<tr> <td class="title"><h2>{item.name}</h2></td> <td>{item.code}</td> <td>{format_price(item.unit_price, self.instance, self.request)}</td> <td>{item.quantity}</td> <td>{format_price(item.subtotal, self.instance, self.request)}</td> <td>{self.format_json(item.extra_rows, self.instance, self.request)}</td> <td>{format_price(item.total, self.instance, self.request)}</td> <td>{self.format_json(item.extra, self.instance, self.request)}</td> </tr>""" return format_html( '<table class="listing full-width">' "<thead>{}</thead>" "<tbody>{}</tbody>" "</table>", mark_safe(head), mark_safe(body), )
[docs] class BoundPanel(ReadOnlyPanel.BoundPanel): def classes(self) -> list[str]: return ["salesman-order-items"] def render_as_field(self) -> str: return self.render() def render_as_object(self) -> str: return self.render() def format_json( self, value: dict[str, Any], obj: Any, request: HttpRequest ) -> str: return app_settings.SALESMAN_ADMIN_JSON_FORMATTER( value, context={"order_item": True} ) def render(self) -> str: head = f"""<tr> <td>{_('Name')}</td> <td>{_('Code')}</td> <td>{_('Unit price')}</td> <td>{_('Quantity')}</td> <td>{_('Subtotal')}</td> <td>{_('Extra rows')}</td> <td>{_('Total')}</td> <td>{_('Extra')}</td> </tr>""" body = "" for item in self.instance.items.all(): body += f"""<tr> <td class="title"><h2>{item.name}</h2></td> <td>{item.code}</td> <td>{format_price(item.unit_price, self.instance, self.request)}</td> <td>{item.quantity}</td> <td>{format_price(item.subtotal, self.instance, self.request)}</td> <td>{self.format_json(item.extra_rows, self.instance, self.request)}</td> <td>{format_price(item.total, self.instance, self.request)}</td> <td>{self.format_json(item.extra, self.instance, self.request)}</td> </tr>""" # noqa return format_html( '<table class="listing full-width">' "<thead>{}</thead>" "<tbody>{}</tbody>" "</table>", mark_safe(head), mark_safe(body), )
[docs]class OrderAdminPanel(ReadOnlyPanel): """ Retrieves value from model_admin which is bound to the form in `get_edit_handler`. """
[docs] def on_model_bound(self) -> None: pass
def on_form_bound(self) -> None: if not hasattr(self.form, "model_admin"): raise AssertionError("OrderAdminPanel can only be used in OrderModelAdmin.") field = getattr(self.form.model_admin, self.attr) heading = getattr(field, "short_description", "") if heading and not self.heading: self.heading = heading def get_value(self) -> Any: return getattr(self.form.model_admin, self.attr)(self.instance)
[docs] class BoundPanel(ReadOnlyPanel.BoundPanel): def __init__( self, panel: Panel, **kwargs: Any, ) -> None: super().__init__(panel, **kwargs) if not hasattr(self.form, "model_admin"): raise AssertionError( "OrderAdminPanel can only be used in OrderModelAdmin." ) field = getattr(self.form.model_admin, self.attr) heading = getattr(field, "short_description", "") if heading and not self.heading: self.heading = heading def get_value(self) -> Any: return getattr(self.form.model_admin, self.attr)(self.instance)