Skip to content

Commit

Permalink
Add PDF export for corrections
Browse files Browse the repository at this point in the history
  • Loading branch information
danielzeljko committed Aug 30, 2023
1 parent c80c2b5 commit 676fbae
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 8 deletions.
41 changes: 38 additions & 3 deletions langcorrect/corrections/utils.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,33 @@
import csv
import logging
import tempfile
from io import StringIO

from django.http import HttpResponse
from django.template.loader import render_to_string
from weasyprint import HTML

from langcorrect.posts.models import PostRow

logger = logging.getLogger(__name__)

CSV_HEADERS = ["Original Sentence", "Corrected Sentence", "Correction Feedback", "Corrector"]
EXCLUDE_TITLE_ROW = 0


class ExportCorrections:
def __init__(self, post) -> None:
self.post = post
self.post_rows = PostRow.available_objects.filter(post=post).exclude(order=0).order_by("created")
self.post_rows = (
PostRow.available_objects.filter(post=post).exclude(order=EXCLUDE_TITLE_ROW).order_by("created")
)

def export_csv(self) -> HttpResponse:
"""Export the post sentences and their corrections to a CSV file."""
output = StringIO()
writer = csv.writer(output)

writer.writerow(["Original Sentence", "Corrected Sentence", "Correction Feedback", "Corrector"])
writer.writerow(CSV_HEADERS)

for post_row in self.post_rows:
for correction in post_row.correctedrow_set.all():
Expand All @@ -27,5 +38,29 @@ def export_csv(self) -> HttpResponse:
yyyy_mm_dd = self.post.created.strftime("%Y-%m-%d")

response = HttpResponse(output.read(), content_type="text/csv")
response["Content-Disposition"] = f"attachment; filename={yyyy_mm_dd}-{self.post.title}.csv"
response["Content-Disposition"] = f"attachment; filename={yyyy_mm_dd}.csv"
return response

def export_pdf(self) -> HttpResponse:
try:
html_string = render_to_string(
"corrections/export_corrections_pdf.html", {"post": self.post, "post_rows": self.post_rows}
)

html = HTML(string=html_string, encoding="utf-8")
result = html.write_pdf()

yyyy_mm_dd = self.post.created.strftime("%Y-%m-%d")

response = HttpResponse(content_type="application/pdf")
response["Content-Disposition"] = f"attachment; filename={yyyy_mm_dd}.pdf"

with tempfile.NamedTemporaryFile(delete=True) as output:
output.write(result)
output.flush()
with open(output.name, "rb") as f:
response.write(f.read())
return response
except Exception as e:
logger.error(f"Failed to export corrections as a PDF. Error: {e}")
return HttpResponse("An error occurred while generating the PDF.")
2 changes: 1 addition & 1 deletion langcorrect/corrections/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def export_corrections(request, slug):
case FileFormat.CSV:
return ExportCorrections(post).export_csv()
case FileFormat.PDF:
pass
return ExportCorrections(post).export_pdf()
case _:
messages.warning(request, translate("Invalid export format specified."))
return redirect(reverse("posts:detail", kwargs={"slug": post.slug}))
67 changes: 67 additions & 0 deletions langcorrect/templates/corrections/export_corrections_pdf.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{% load i18n %}
{% load humanize %}
{% load static %}

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We"
crossorigin="anonymous" />
<title>{{ post.title }}</title>
</head>
<body>
<style>
* {
font-size: 12px;
}
</style>
<div class="card">
<div class="card-header bg-transparent text-center">
{{ post.title }} by {{ post.user.username }} ({{ post.created|naturalday }})
</div>
<div class="card-body">
<div class="row">
<div class="col-6">
<p>Original</p>
<p>{{ post.text }}</p>
</div>
<div class="col-6">
<p>Notes</p>
<p>{{ post.native_text }}</p>
</div>
</div>
</div>
</div>
<div class="card mt-2">
<div class="card-header bg-transparent text-center">Corrections</div>
<div class="card-body">
{% for post_row in post_rows %}
{% if post_row.correctedrow_set.all %}
<div class="row border-bottom mb-1">
<div class="col-6">
<p>{{ post_row.sentence }}</p>
</div>
<div class="col-6">
{% if post_row.perfectrow_set.all.count > 0 %}
<p>This sentence has been marked as perfect! x{{ post_row.perfectrow_set.all.count }}</p>
{% endif %}
<div class="d-grid gap-2">
{% for correction in post_row.correctedrow_set.all %}
<div>
<p class="mb-0">{{ correction.display_correction|safe }}</p>
<p class="text-muted">{{ correction.note }}</p>
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}
{% endfor %}
</div>
</div>
</body>
</html>
7 changes: 3 additions & 4 deletions langcorrect/templates/modals/export_corrections.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,10 @@ <h1 class="modal-title fs-5" id="exportModalLabel">{% translate "Export correcti
class="btn btn-outline-secondary">
<i class="fas fa-file-csv"></i> CSV
</a>
<button type="button"
class="btn btn-outline-secondary"
data-bs-dismiss="modal">
<a href="{% url 'posts:export-corrections' post.slug %}?format=PDF"
class="btn btn-outline-secondary">
<i class="fas fa-file-pdf"></i> PDF
</button>
</a>
</div>
</div>
</div>
Expand Down
2 changes: 2 additions & 0 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ djangorestframework==3.14.0 # https://github.com/encode/django-rest-framework
django-cors-headers==4.2.0 # https://github.com/adamchainz/django-cors-headers
# DRF-spectacular for api documentation
drf-spectacular==0.26.3 # https://github.com/tfranzel/drf-spectacular

weasyprint==59.0 # https://doc.courtbouillon.org/weasyprint/

0 comments on commit 676fbae

Please sign in to comment.