Skip to content

Commit f0e2a2b

Browse files
committed
♻️(backend) automatic delete temporary files
To leverage the automatic deletion of temporary files, we do the conversion inside the with context. Even if the conversion fails, the temporary file will be deleted.
1 parent 67625df commit f0e2a2b

File tree

2 files changed

+44
-15
lines changed

2 files changed

+44
-15
lines changed

src/backend/core/models.py

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
"""
44

55
import hashlib
6-
import os
76
import tempfile
87
import textwrap
98
import uuid
@@ -609,33 +608,33 @@ def generate_word(self, body_html, metadata):
609608
"""
610609

611610
reference_docx = "core/static/reference.docx"
611+
output = BytesIO()
612612

613613
# Convert the HTML to a temporary docx file
614-
with tempfile.NamedTemporaryFile(delete=False, suffix=".docx") as tmp_file:
614+
with tempfile.NamedTemporaryFile(suffix=".docx", prefix="docx_") as tmp_file:
615615
output_path = tmp_file.name
616616

617-
pypandoc.convert_text(
618-
html_string,
619-
"docx",
620-
format="html",
621-
outputfile=output_path,
622-
extra_args=["--reference-doc", reference_docx],
623-
)
624-
625-
# Create a BytesIO object to store the output of the temporary docx file
626-
with open(output_path, "rb") as f:
627-
output = BytesIO(f.read())
617+
pypandoc.convert_text(
618+
html_string,
619+
"docx",
620+
format="html",
621+
outputfile=output_path,
622+
extra_args=["--reference-doc", reference_docx],
623+
)
628624

629-
# Remove the temporary docx file
630-
os.remove(output_path)
625+
# Create a BytesIO object to store the output of the temporary docx file
626+
with open(output_path, "rb") as f:
627+
output = BytesIO(f.read())
631628

629+
# Ensure the pointer is at the beginning
632630
output.seek(0)
633631

634632
response = FileResponse(
635633
output,
636634
content_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
637635
)
638636
response["Content-Disposition"] = f"attachment; filename={self.title}.docx"
637+
639638
return response
640639

641640
def generate_document(self, body, body_type, export_format):

src/backend/core/tests/test_models_templates.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
Unit tests for the Template model
33
"""
44

5+
import os
6+
from unittest import mock
7+
58
from django.contrib.auth.models import AnonymousUser
69
from django.core.exceptions import ValidationError
710

@@ -185,3 +188,30 @@ def test_models_templates_get_abilities_preset_role(django_assert_num_queries):
185188
"partial_update": False,
186189
"generate_document": True,
187190
}
191+
192+
193+
def test_models_templates__generate_word():
194+
"""Generate word document and assert no tmp files are left in /tmp folder."""
195+
template = factories.TemplateFactory()
196+
response = template.generate_word("<p>Test body</p>", {})
197+
198+
assert response.status_code == 200
199+
assert len([f for f in os.listdir("/tmp") if f.startswith("docx_")]) == 0
200+
201+
202+
@mock.patch(
203+
"pypandoc.convert_text",
204+
side_effect=RuntimeError("Conversion failed"),
205+
)
206+
def test_models_templates__generate_word__raise_error(_mock_send_mail):
207+
"""
208+
Generate word document and assert no tmp files are left in /tmp folder
209+
even when the conversion fails.
210+
"""
211+
template = factories.TemplateFactory()
212+
213+
try:
214+
template.generate_word("<p>Test body</p>", {})
215+
except RuntimeError as e:
216+
assert str(e) == "Conversion failed"
217+
assert len([f for f in os.listdir("/tmp") if f.startswith("docx_")]) == 0

0 commit comments

Comments
 (0)