3
0
Fork 0

fix: add WebDiff class now that Diff lives in wuttjamaican

This commit is contained in:
Lance Edgar 2025-12-20 18:38:49 -06:00
parent 7e0b16c57d
commit 9a7488b063
3 changed files with 26 additions and 214 deletions

View file

@ -29,60 +29,20 @@ import sqlalchemy as sa
from pyramid.renderers import render from pyramid.renderers import render
from webhelpers2.html import HTML from webhelpers2.html import HTML
from wuttjamaican.diffs import Diff
class Diff:
class WebDiff(Diff):
""" """
Represent / display a basic "diff" between two data records. Simple diff class for the web app.
You must provide both the "old" and "new" data records, when This is based on the
constructing an instance of this class. Then call :class:`~wuttjamaican:wuttjamaican.diffs.Diff` class; it just
:meth:`render_html()` to display the diff table. tweaks :meth:`render_html()` to use the web template lookup
engine.
:param old_data: Dict of "old" data record.
:param new_data: Dict of "new" data record.
:param fields: Optional list of field names. If not specified,
will be derived from the data records.
:param nature: What sort of diff is being represented; must be one
of: ``("create", "update", "delete")``
:param old_color: Background color to display for "old/deleted"
field data, when applicable.
:param new_color: Background color to display for "new/created"
field data, when applicable.
""" """
def __init__( # pylint: disable=too-many-arguments,too-many-positional-arguments cell_padding = None
self,
old_data: dict,
new_data: dict,
fields: list = None,
nature="update",
old_color="#ffebe9",
new_color="#dafbe1",
):
self.old_data = old_data
self.new_data = new_data
self.columns = ["field name", "old value", "new value"]
self.fields = fields or self.make_fields()
self.nature = nature
self.old_color = old_color
self.new_color = new_color
def make_fields(self): # pylint: disable=missing-function-docstring
return sorted(set(self.old_data) | set(self.new_data), key=lambda x: x.lower())
def old_value(self, field): # pylint: disable=missing-function-docstring
return self.old_data.get(field)
def new_value(self, field): # pylint: disable=missing-function-docstring
return self.new_data.get(field)
def values_differ(self, field): # pylint: disable=missing-function-docstring
return self.new_value(field) != self.old_value(field)
def render_html(self, template="/diff.mako", **kwargs): def render_html(self, template="/diff.mako", **kwargs):
""" """
@ -98,69 +58,25 @@ class Diff:
""" """
context = kwargs context = kwargs
context["diff"] = self context["diff"] = self
return HTML.literal(render(template, context)) html = render(template, context)
return HTML.literal(html)
def render_field_row(self, field): # pylint: disable=missing-function-docstring
is_diff = self.values_differ(field)
td_field = HTML.tag("td", class_="field", c=field)
td_old_value = HTML.tag(
"td",
c=self.render_old_value(field),
**self.get_old_value_attrs(is_diff),
)
td_new_value = HTML.tag(
"td",
c=self.render_new_value(field),
**self.get_new_value_attrs(is_diff),
)
return HTML.tag("tr", c=[td_field, td_old_value, td_new_value])
def render_old_value(self, field): # pylint: disable=missing-function-docstring
value = self.old_value(field)
return repr(value)
def render_new_value(self, field): # pylint: disable=missing-function-docstring
value = self.new_value(field)
return repr(value)
def get_old_value_attrs( # pylint: disable=missing-function-docstring
self, is_diff
):
attrs = {}
if self.nature == "update" and is_diff:
attrs["style"] = f"background-color: {self.old_color};"
elif self.nature == "delete":
attrs["style"] = f"background-color: {self.old_color};"
return attrs
def get_new_value_attrs( # pylint: disable=missing-function-docstring
self, is_diff
):
attrs = {}
if self.nature == "create":
attrs["style"] = f"background-color: {self.new_color};"
elif self.nature == "update" and is_diff:
attrs["style"] = f"background-color: {self.new_color};"
return attrs
class VersionDiff(Diff): class VersionDiff(WebDiff):
""" """
Special diff class for use with version history views. While Special diff class for use with version history views. While
based on :class:`Diff`, this class uses a different signature for based on :class:`WebDiff`, this class uses a different signature
the constructor. for the constructor.
:param version: Reference to a Continuum version record (object). :param config: The app :term:`config object`.
:param version: Reference to a Continuum version record object.
:param \\**kwargs: Remaining kwargs are passed as-is to the :param \\**kwargs: Remaining kwargs are passed as-is to the
:class:`Diff` constructor. :class:`WebDiff` constructor.
""" """
def __init__(self, version, **kwargs): def __init__(self, config, version, **kwargs):
import sqlalchemy_continuum as continuum # pylint: disable=import-outside-toplevel import sqlalchemy_continuum as continuum # pylint: disable=import-outside-toplevel
from wutta_continuum.util import ( # pylint: disable=import-outside-toplevel from wutta_continuum.util import ( # pylint: disable=import-outside-toplevel
render_operation_type, render_operation_type,
@ -195,7 +111,7 @@ class VersionDiff(Diff):
old_data[field] = getattr(version.previous, field) old_data[field] = getattr(version.previous, field)
new_data[field] = getattr(version, field) new_data[field] = getattr(version, field)
super().__init__(old_data, new_data, **kwargs) super().__init__(config, old_data, new_data, **kwargs)
def get_default_fields(self): # pylint: disable=missing-function-docstring def get_default_fields(self): # pylint: disable=missing-function-docstring
fields = sorted(self.version_mapper.columns.keys()) fields = sorted(self.version_mapper.columns.keys())

View file

@ -1269,7 +1269,7 @@ class MasterView(View): # pylint: disable=too-many-public-methods
) )
version_diffs = [ version_diffs = [
VersionDiff(version) VersionDiff(self.config, version)
for version in self.get_relevant_versions(txn, instance) for version in self.get_relevant_versions(txn, instance)
] ]

View file

@ -4,114 +4,10 @@ from wuttaweb import diffs as mod
from wuttaweb.testing import WebTestCase, VersionWebTestCase from wuttaweb.testing import WebTestCase, VersionWebTestCase
# nb. using WebTestCase here only for mako support in render_html() class TestWebDiff(WebTestCase):
class TestDiff(WebTestCase):
def make_diff(self, *args, **kwargs): def make_diff(self, *args, **kwargs):
return mod.Diff(*args, **kwargs) return mod.WebDiff(self.config, *args, **kwargs)
def test_constructor(self):
old_data = {"foo": "bar"}
new_data = {"foo": "baz"}
diff = self.make_diff(old_data, new_data, fields=["foo"])
self.assertEqual(diff.fields, ["foo"])
def test_make_fields(self):
old_data = {"foo": "bar"}
new_data = {"foo": "bar", "baz": "zer"}
# nb. this calls make_fields()
diff = self.make_diff(old_data, new_data)
# TODO: should the fields be cumulative? or just use new_data?
self.assertEqual(diff.fields, ["baz", "foo"])
def test_values(self):
old_data = {"foo": "bar"}
new_data = {"foo": "baz"}
diff = self.make_diff(old_data, new_data)
self.assertEqual(diff.old_value("foo"), "bar")
self.assertEqual(diff.new_value("foo"), "baz")
def test_values_differ(self):
old_data = {"foo": "bar"}
new_data = {"foo": "baz"}
diff = self.make_diff(old_data, new_data)
self.assertTrue(diff.values_differ("foo"))
old_data = {"foo": "bar"}
new_data = {"foo": "bar"}
diff = self.make_diff(old_data, new_data)
self.assertFalse(diff.values_differ("foo"))
def test_render_values(self):
old_data = {"foo": "bar"}
new_data = {"foo": "baz"}
diff = self.make_diff(old_data, new_data)
self.assertEqual(diff.render_old_value("foo"), "'bar'")
self.assertEqual(diff.render_new_value("foo"), "'baz'")
def test_get_old_value_attrs(self):
# no change
old_data = {"foo": "bar"}
new_data = {"foo": "bar"}
diff = self.make_diff(old_data, new_data, nature="update")
self.assertEqual(diff.get_old_value_attrs(False), {})
# update
old_data = {"foo": "bar"}
new_data = {"foo": "baz"}
diff = self.make_diff(old_data, new_data, nature="update")
self.assertEqual(
diff.get_old_value_attrs(True),
{"style": f"background-color: {diff.old_color};"},
)
# delete
old_data = {"foo": "bar"}
new_data = {}
diff = self.make_diff(old_data, new_data, nature="delete")
self.assertEqual(
diff.get_old_value_attrs(True),
{"style": f"background-color: {diff.old_color};"},
)
def test_get_new_value_attrs(self):
# no change
old_data = {"foo": "bar"}
new_data = {"foo": "bar"}
diff = self.make_diff(old_data, new_data, nature="update")
self.assertEqual(diff.get_new_value_attrs(False), {})
# update
old_data = {"foo": "bar"}
new_data = {"foo": "baz"}
diff = self.make_diff(old_data, new_data, nature="update")
self.assertEqual(
diff.get_new_value_attrs(True),
{"style": f"background-color: {diff.new_color};"},
)
# create
old_data = {}
new_data = {"foo": "bar"}
diff = self.make_diff(old_data, new_data, nature="create")
self.assertEqual(
diff.get_new_value_attrs(True),
{"style": f"background-color: {diff.new_color};"},
)
def test_render_field_row(self):
old_data = {"foo": "bar"}
new_data = {"foo": "baz"}
diff = self.make_diff(old_data, new_data)
row = diff.render_field_row("foo")
self.assertIn("<tr>", row)
self.assertIn("&#39;bar&#39;", row)
self.assertIn(f'style="background-color: {diff.old_color};"', row)
self.assertIn("&#39;baz&#39;", row)
self.assertIn(f'style="background-color: {diff.new_color};"', row)
self.assertIn("</tr>", row)
def test_render_html(self): def test_render_html(self):
old_data = {"foo": "bar"} old_data = {"foo": "bar"}
@ -121,9 +17,9 @@ class TestDiff(WebTestCase):
self.assertIn("<table", html) self.assertIn("<table", html)
self.assertIn("<tr>", html) self.assertIn("<tr>", html)
self.assertIn("&#39;bar&#39;", html) self.assertIn("&#39;bar&#39;", html)
self.assertIn(f'style="background-color: {diff.old_color};"', html) self.assertIn(f'style="background-color: {diff.old_color}"', html)
self.assertIn("&#39;baz&#39;", html) self.assertIn("&#39;baz&#39;", html)
self.assertIn(f'style="background-color: {diff.new_color};"', html) self.assertIn(f'style="background-color: {diff.new_color}"', html)
self.assertIn("</tr>", html) self.assertIn("</tr>", html)
self.assertIn("</table>", html) self.assertIn("</table>", html)
@ -131,7 +27,7 @@ class TestDiff(WebTestCase):
class TestVersionDiff(VersionWebTestCase): class TestVersionDiff(VersionWebTestCase):
def make_diff(self, *args, **kwargs): def make_diff(self, *args, **kwargs):
return mod.VersionDiff(*args, **kwargs) return mod.VersionDiff(self.config, *args, **kwargs)
def test_constructor(self): def test_constructor(self):
import sqlalchemy_continuum as continuum import sqlalchemy_continuum as continuum