Coverage for website/newsletters/services.py: 68.00%
65 statements
« prev ^ index » next coverage.py v7.6.7, created at 2025-08-14 10:31 +0000
« prev ^ index » next coverage.py v7.6.7, created at 2025-08-14 10:31 +0000
1import base64
2import logging
3from io import BytesIO
5from django.core.files.base import ContentFile
6from django.template.loader import get_template
7from django.utils import timezone
9import requests
10from bs4 import BeautifulSoup
11from PIL import Image
13from events.models import Event
14from newsletters import emails
15from partners.models import Partner
17from .signals import sent_newsletter
19logger = logging.getLogger(__name__)
22def save_to_disk(newsletter):
23 """Write the newsletter as HTML to file (in all languages)."""
24 main_partner = Partner.objects.filter(is_main_partner=True).first()
25 local_partners = split_local_partners()
27 html_template = get_template("newsletters/email.html")
29 context = {
30 "newsletter": newsletter,
31 "agenda_events": (
32 newsletter.newslettercontent_set.filter(newsletteritem=None).order_by(
33 "newsletterevent__start_datetime"
34 )
35 ),
36 "main_partner": main_partner,
37 "local_partners": local_partners,
38 }
40 html_message = html_template.render(context)
41 html_message = embed_linked_html_images(html_message)
43 newsletter.rendered_file = ContentFile(
44 html_message.encode("utf-8"), name="newsletter.html"
45 )
46 newsletter.save()
49def embed_linked_html_images(html_input):
50 bs = BeautifulSoup(html_input, "html.parser")
52 output = html_input
53 images = bs.findAll("img")
55 for image in images:
56 source = image["src"]
57 if not "source".startswith(("http://", "https://")): 57 ↛ 59line 57 didn't jump to line 59 because the condition on line 57 was always true
58 continue
59 try:
60 response = requests.get(source, timeout=30.0)
61 image = Image.open(BytesIO(response.content))
62 buffer = BytesIO()
63 image.save(buffer, format="png")
64 base64_image = base64.b64encode(buffer.getvalue()).decode("utf-8")
65 encoded_image = "data:image/png;base64," + base64_image
66 output = output.replace(source, encoded_image)
67 except OSError:
68 logger.warning(f"Image could not be found: {image}")
70 return output
73def get_agenda(start_date):
74 end_date = start_date + timezone.timedelta(weeks=2)
75 published_events = Event.objects.filter(published=True)
76 base_events = published_events.filter(
77 start__gte=start_date, end__lt=end_date
78 ).order_by("start")
79 if base_events.count() < 10:
80 more_events = published_events.filter(end__gte=end_date).order_by("start")
81 return [*base_events, *more_events][:10]
82 return base_events
85def send_newsletter(newsletter):
86 emails.send_newsletter(newsletter)
87 newsletter.sent = True
88 newsletter.save()
90 sent_newsletter.send(sender=None, newsletter=newsletter)
92 save_to_disk(newsletter)
95def split_local_partners():
96 all_local_partners = Partner.objects.filter(
97 is_local_partner=True, is_active=True
98 ).order_by("?")
99 local_partner_count = len(all_local_partners)
100 local_partners = []
101 for i in range(local_partner_count // 2): 101 ↛ 102line 101 didn't jump to line 102 because the loop on line 101 never started
102 local_partners.append(
103 [all_local_partners[i * 2], all_local_partners[i * 2 + 1]]
104 )
106 if local_partner_count % 2 != 0: 106 ↛ 107line 106 didn't jump to line 107 because the condition on line 106 was never true
107 local_partners.append([all_local_partners[local_partner_count - 1]])
109 return local_partners