Skip to content

Commit 573e01b

Browse files
authored
HTML-to-Text conversion removed linebreaks and links (#35)
1 parent c0624a2 commit 573e01b

File tree

5 files changed

+33
-14
lines changed

5 files changed

+33
-14
lines changed

CHANGES.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
**2.7.1** (2025-05-29)
4+
* Fixed a bug where html-converted plain text would miss links
5+
36
**2.7.0** (2025-05-21)
47
* Replaced `html2text` with `beautifulsoup4` to make package available under MIT license
58
* Switched back to MIT license
@@ -13,7 +16,7 @@
1316
* Maintenance updates via ambient-package-update
1417

1518
* *2.6.1* (2025-03-18)
16-
* Fixed a bug, where translations were not deactivated after sending an email
19+
* Fixed a bug where translations were not deactivated after sending an email
1720

1821
* *2.6.0* (2025-03-17)
1922
* Added check for email structure validity

django_pony_express/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Class-based emails including a test suite for Django"""
22

3-
__version__ = "2.7.0"
3+
__version__ = "2.7.1"

django_pony_express/services/base.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,15 @@ def _generate_html_content(self, mail_attributes: dict) -> str:
242242
def _generate_text_content(self, mail_attributes: dict, html_content: str) -> str:
243243
# Render TXT body part if a template is explicitly set, otherwise convert HTML template to plain text
244244
if not self.template_txt_name:
245-
soup = BeautifulSoup(html_content)
246-
return soup.get_text(strip=True)
245+
soup = BeautifulSoup(html_content, "html.parser")
246+
247+
# Convert links to this pattern: "[LINK NAME] ([LINK URL])"
248+
for a in soup.find_all("a"):
249+
text = a.get_text()
250+
href = a.get("href", "")
251+
a.replace_with(f"{text} ({href})")
252+
253+
return soup.get_text(separator="\n", strip=True)
247254
else:
248255
return render_to_string(self.template_txt_name, mail_attributes)
249256

testapp/templates/testapp/test_email.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
<div>
44
Current date test: {% now "l, d. F Y" %}
55
</div>
6+
<div>
7+
<a href="{{ link_url }}">I am a link</a>
8+
</div>
69
<div>
710
Variable test: {{ my_var }}
811
</div>

tests/services/base/test_base_mail_service.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -475,23 +475,29 @@ def test_text_templates_rendering(self):
475475
my_var = "Lorem ipsum dolor!"
476476
service = BaseEmailService()
477477
service.template_txt_name = "testapp/test_email.txt"
478-
msg_html = service._generate_text_content({"my_var": my_var}, "")
478+
msg_text = service._generate_text_content({"my_var": my_var}, "")
479479

480480
# Assertions
481-
self.assertIsInstance(msg_html, str)
481+
self.assertIsInstance(msg_text, str)
482482

483-
self.assertIn("Lorem ipsum dolor", msg_html)
484-
self.assertIn("I am a different content", msg_html)
485-
self.assertNotIn("Current date test", msg_html)
483+
self.assertIn("Lorem ipsum dolor", msg_text)
484+
self.assertIn("I am a different content", msg_text)
485+
self.assertNotIn("Current date test", msg_text)
486486

487-
def test_text_templates_rendering_fallback(self):
487+
def test_generate_text_content_html_conversion(self):
488488
my_var = "Lorem ipsum dolor!"
489489
service = BaseEmailService()
490-
msg_html = service._generate_text_content({"my_var": my_var}, "Lorem ipsum dolor")
490+
service.template_name = "testapp/test_email.html"
491+
492+
msg_html = service._generate_html_content({"my_var": my_var, "link_url": "https//example.com?pony=horse"})
493+
msg_txt = service._generate_text_content(mail_attributes={}, html_content=msg_html)
491494

492495
# Assertions
493496
self.assertIsInstance(msg_html, str)
494497

495-
self.assertIn("Lorem ipsum dolor", msg_html)
496-
self.assertNotIn("I am a different content", msg_html)
497-
self.assertNotIn("Current date test", msg_html)
498+
self.assertIn("Lorem ipsum dolor", msg_txt)
499+
# Check linebreaks are not removed
500+
self.assertIn("29. May 2025\n", msg_txt)
501+
502+
# Check links are converted correctly
503+
self.assertIn("I am a link (https//example.com?pony=horse)", msg_txt)

0 commit comments

Comments
 (0)