diff --git a/CHANGELOG.md b/CHANGELOG.md
index 63eca3a..9202116 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,16 +5,6 @@ All notable changes to wuttaweb will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
-## v0.29.3 (2026-03-15)
-
-### Fix
-
-- fix datetime handling for alembic migrations view
-- keep original value along with rendered, in grid vue context
-- render human-friendly datetime for such fields in version diff
-- remove version pin for setuptools dependency
-- make pylint happy
-
## v0.29.2 (2026-03-10)
### Fix
diff --git a/pyproject.toml b/pyproject.toml
index 25183bd..2613603 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
[project]
name = "WuttaWeb"
-version = "0.29.3"
+version = "0.29.2"
description = "Web App for Wutta Framework"
readme = "README.md"
authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]
@@ -52,6 +52,11 @@ dependencies = [
# can address a new bug that showed up in SA 2.1.0b1
# cf. https://github.com/kvesteri/sqlalchemy-utils/issues/800
"SQLAlchemy<2.1",
+
+ # nb. this must be pinned for now, until pyramid can remove
+ # its dependency on pkg_resources.
+ # cf. https://github.com/Pylons/pyramid/issues/3731
+ "setuptools<81",
]
diff --git a/src/wuttaweb/diffs.py b/src/wuttaweb/diffs.py
index 23eaa7e..cb59ed5 100644
--- a/src/wuttaweb/diffs.py
+++ b/src/wuttaweb/diffs.py
@@ -25,7 +25,6 @@ Tools for displaying simple data diffs
"""
import sqlalchemy as sa
-from sqlalchemy import orm
from pyramid.renderers import render
from webhelpers2.html import HTML
@@ -151,25 +150,6 @@ class VersionDiff(WebDiff):
# be embedded within a more complex result.
text = HTML.tag("span", c=[repr(value)], style="font-family: monospace;")
- # style to apply for bold human-friendly text (if applicable)
- bold = "margin-left: 2rem; font-style: italic; font-weight: bold;"
-
- # check for standard datetime field
- if self.mapper.has_property(field):
- prop = self.mapper.get_property(field)
- if isinstance(prop, orm.ColumnProperty):
- if len(prop.columns) == 1:
- col = prop.columns[0]
- if isinstance(col.type, sa.DateTime):
- if value:
- # render as local datetime w/ "time since" tooltip
- display = HTML.tag(
- "span",
- c=self.app.render_datetime(value, html=True),
- style=bold,
- )
- return HTML.tag("span", c=[text, display])
-
# loop thru all mapped relationship props
for prop in self.mapper.relationships:
@@ -196,9 +176,12 @@ class VersionDiff(WebDiff):
if ref := getattr(ref, "version_parent", None):
# render text w/ related object as bold string
+ style = (
+ "margin-left: 2rem; font-style: italic; font-weight: bold;"
+ )
return HTML.tag(
"span",
- c=[text, HTML.tag("span", c=[str(ref)], style=bold)],
+ c=[text, HTML.tag("span", c=[str(ref)], style=style)],
)
return text
diff --git a/src/wuttaweb/grids/base.py b/src/wuttaweb/grids/base.py
index e2a9f83..1906997 100644
--- a/src/wuttaweb/grids/base.py
+++ b/src/wuttaweb/grids/base.py
@@ -2501,10 +2501,11 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth
# loop thru data
data = []
row_classes = {}
- for i, original_record in enumerate(original_data, 1):
+ for i, record in enumerate(original_data, 1):
+ original_record = record
# convert record to new dict
- record = self.object_to_dict(original_record)
+ record = self.object_to_dict(record)
# discard non-declared fields
record = {field: record[field] for field in record if field in self.columns}
@@ -2517,17 +2518,19 @@ class Grid: # pylint: disable=too-many-instance-attributes,too-many-public-meth
# nb. no need to render if column not included
if key in self.columns:
value = record.get(key, None)
- record[f"_rendered_{key}"] = renderer(original_record, key, value)
+ record[key] = renderer(original_record, key, value)
# add action urls to each record
for action in self.actions:
key = f"_action_url_{action.key}"
if key not in record:
- if url := action.get_url(original_record, i):
+ url = action.get_url(original_record, i)
+ if url:
record[key] = url
# set row css class if applicable
- if css_class := self.get_row_class(original_record, record, i):
+ css_class = self.get_row_class(original_record, record, i)
+ if css_class:
# nb. use *string* zero-based index, for js compat
row_classes[str(i - 1)] = css_class
diff --git a/src/wuttaweb/templates/grids/vue_template.mako b/src/wuttaweb/templates/grids/vue_template.mako
index 34424e9..47720fa 100644
--- a/src/wuttaweb/templates/grids/vue_template.mako
+++ b/src/wuttaweb/templates/grids/vue_template.mako
@@ -177,9 +177,9 @@
cell-class="c_${column['field']}">
% if grid.is_linked(column['field']):
+ v-html="props.row.${column['field']}" />
% else:
-
+
% endif
${b}-table-column>
% endif
diff --git a/src/wuttaweb/views/alembic.py b/src/wuttaweb/views/alembic.py
index 5d73849..ed9352e 100644
--- a/src/wuttaweb/views/alembic.py
+++ b/src/wuttaweb/views/alembic.py
@@ -44,23 +44,19 @@ from webhelpers2.html import tags, HTML
from wuttaweb.views import View, MasterView
from wuttaweb.forms import widgets
-from wuttaweb.forms.schema import WuttaDateTime
log = logging.getLogger(__name__)
-def normalize_revision(
- config, rev, json_safe=False
-): # pylint: disable=missing-function-docstring
+def normalize_revision(config, rev): # pylint: disable=missing-function-docstring
app = config.get_app()
created = None
if match := re.search(r"Create Date: (\d{4}-\d{2}-\d{2}[\d:\. ]+\d)", rev.longdoc):
created = datetime.datetime.fromisoformat(match.group(1))
created = app.localtime(created, from_utc=False)
- if json_safe:
- created = app.render_datetime(created)
+ created = app.render_datetime(created)
return {
"revision": rev.revision,
@@ -96,7 +92,7 @@ class AlembicDashboardView(View):
current_heads = context.get_current_heads()
def normalize(rev):
- normal = normalize_revision(self.config, rev, json_safe=True)
+ normal = normalize_revision(self.config, rev)
normal["is_current"] = rev.revision in current_heads
normal["revision"] = tags.link_to(
@@ -322,9 +318,6 @@ class AlembicMigrationView(MasterView): # pylint: disable=abstract-method
g.set_label("is_head", "Head")
g.set_renderer("is_head", self.render_is_head)
- # created
- g.set_renderer("created", "datetime")
-
def render_is_head( # pylint: disable=missing-function-docstring,unused-argument
self, rev, field, value
):
@@ -376,10 +369,6 @@ class AlembicMigrationView(MasterView): # pylint: disable=abstract-method
# path
f.set_widget("path", widgets.CopyableTextWidget())
- # created
- f.set_node("created", WuttaDateTime())
- f.set_widget("created", widgets.WuttaDateTimeWidget(self.request))
-
def make_create_form(self): # pylint: disable=empty-docstring
""" """
alembic = make_alembic_config(self.config)
diff --git a/tests/grids/test_base.py b/tests/grids/test_base.py
index 2978364..15a7a06 100644
--- a/tests/grids/test_base.py
+++ b/tests/grids/test_base.py
@@ -2025,12 +2025,7 @@ class TestGrid(WebTestCase):
context,
{
"data": [
- {
- "foo": "bar",
- "_rendered_foo": "blah blah",
- "baz": "zoo",
- "_action_url_view": "/blarg",
- }
+ {"foo": "blah blah", "baz": "zoo", "_action_url_view": "/blarg"}
],
"row_classes": {},
},
@@ -2085,16 +2080,7 @@ class TestGrid(WebTestCase):
# can override value rendering
grid.set_renderer("foo", lambda record, key, value: "blah blah")
data = grid.get_vue_data()
- self.assertEqual(
- data,
- [
- {
- "foo": "bar",
- "_rendered_foo": "blah blah",
- "_action_url_view": "/blarg",
- }
- ],
- )
+ self.assertEqual(data, [{"foo": "blah blah", "_action_url_view": "/blarg"}])
def test_get_row_class(self):
model = self.app.model
diff --git a/tests/test_diffs.py b/tests/test_diffs.py
index 6f66893..00c78a9 100644
--- a/tests/test_diffs.py
+++ b/tests/test_diffs.py
@@ -1,14 +1,5 @@
# -*- coding: utf-8; -*-
-import datetime
-import re
-from unittest.mock import patch
-
-import sqlalchemy as sa
-from sqlalchemy import orm
-
-from wuttjamaican.util import get_timezone_by_name
-
from wuttaweb import diffs as mod
from wuttaweb.testing import WebTestCase, VersionWebTestCase
@@ -79,7 +70,7 @@ class TestVersionDiff(VersionWebTestCase):
["active", "person_uuid", "prevent_edit", "username", "uuid"],
)
- def test_render_version_value_with_objref(self):
+ def test_render_version_value(self):
import sqlalchemy_continuum as continuum
model = self.app.model
@@ -139,91 +130,3 @@ class TestVersionDiff(VersionWebTestCase):
self.assertIn(str(person.uuid), html)
self.assertIn("Fred Flintstone", html)
self.assertEqual(diff.render_new_value("person_uuid"), "")
-
- def test_render_version_value_with_datetime(self):
- import sqlalchemy_continuum as continuum
-
- tzlocal = get_timezone_by_name("America/Los_Angeles")
- with patch.object(self.app, "get_timezone", return_value=tzlocal):
-
- # make one person
- model = self.app.model
- person = model.Person(full_name="Fred Flintstone")
- self.session.add(person)
- self.session.commit()
-
- # get its one version record
- txncls = continuum.transaction_class(model.Person)
- vercls = continuum.version_class(model.Person)
- version = self.session.query(vercls).order_by(vercls.transaction_id).one()
-
- # make a diff, but we have to mock some things up for the test
- # coverage, since we don't currently have a versioned datetime
- # field in the base model.
- diff = self.make_diff(version)
-
- mock_column = sa.Column("timestamp", sa.DateTime())
- mock_property = orm.ColumnProperty(column=mock_column)
-
- class MockMapper:
- def __init__(self, mapper):
- self.mapper = mapper
-
- def __getattr__(self, name):
- if hasattr(self.mapper, name):
- return getattr(self.mapper, name)
- raise AttributeError(f"attr not found: {name}")
-
- def has_property(self, field):
- if field == "timestamp":
- return True
- return self.mapper.has_property(field)
-
- def get_property(self, field):
- if field == "timestamp":
- return mock_property
- return self.mapper.get_property(field)
-
- mock_mapper = MockMapper(diff.mapper)
- with patch.object(diff, "mapper", new=mock_mapper):
-
- # zone-aware local time
- dt = datetime.datetime(2026, 3, 14, 14, 0, tzinfo=tzlocal)
- html = diff.render_version_value(version, "timestamp", dt)
- self.assertTrue(
- re.search(
- r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}-\d{4}',
- html,
- )
- )
- self.assertTrue(
- re.search(
- r'2026-03-14 14:00-0700',
- html,
- )
- )
-
- # zone-naive (presumed UTC)
- dt = datetime.datetime(2026, 3, 14, 21, 0)
- html = diff.render_version_value(version, "timestamp", dt)
- self.assertTrue(
- re.search(
- r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}-\d{4}',
- html,
- )
- )
- self.assertTrue(
- re.search(
- r'2026-03-14 14:00-0700',
- html,
- )
- )
-
- # null
- html = diff.render_version_value(version, "timestamp", None)
- self.assertFalse(
- re.search(
- r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}-\d{4}',
- html,
- )
- )