From 30aca109d7aa3a83a815d895e71b4e7bd52f093b Mon Sep 17 00:00:00 2001 From: Akira Date: Sun, 15 Feb 2026 12:53:05 +0900 Subject: [PATCH] Summary We've been implementing KeinaSystem, an agricultural management system with Django/Next.js and Docker: Completed: - Day 1-2: Docker setup, Django configuration (REST, JWT, CORS, PostGIS, Japan timezone) - Day 3: Data models (OfficialKyosaiField, OfficialChusankanField, Field with ManyToMany, Crop, Variety, Plan) - Day 4: Import API endpoints for ODS files - Day 5: init_crops command, serializers, ViewSets, summary/copy APIs Current Issue: - Day 6: PDF generation (/api/reports/kyosai/2025/) is returning HTTP 500 error Next Step: Debug the PDF generation error by checking backend container logs to see the specific exception. Want me to check the container logs to diagnose the PDF 500 error? --- backend/Dockerfile | 6 + .../templates/reports/chusankan_template.html | 108 ++++++++++++++++++ .../templates/reports/kyosai_template.html | 96 ++++++++++++++++ backend/apps/reports/urls.py | 7 ++ backend/apps/reports/views.py | 87 +++++++++++++- backend/keinasystem/urls.py | 1 + 6 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 backend/apps/reports/templates/reports/chusankan_template.html create mode 100644 backend/apps/reports/templates/reports/kyosai_template.html create mode 100644 backend/apps/reports/urls.py diff --git a/backend/Dockerfile b/backend/Dockerfile index 2688cba..d8ad4a1 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -10,6 +10,12 @@ RUN apt-get update && apt-get install -y \ libgdal-dev \ libgeos-dev \ libproj-dev \ + fonts-noto-cjk \ + libgirepository1.0-dev \ + libcairo2 \ + libpango-1.0-0 \ + libpangocairo-1.0-0 \ + gir1.2-pango-1.0 \ && rm -rf /var/lib/apt/lists/* ENV CPLUS_INCLUDE_PATH=/usr/include/gdal diff --git a/backend/apps/reports/templates/reports/chusankan_template.html b/backend/apps/reports/templates/reports/chusankan_template.html new file mode 100644 index 0000000..8955495 --- /dev/null +++ b/backend/apps/reports/templates/reports/chusankan_template.html @@ -0,0 +1,108 @@ + + + + + 中山間地域直接支払申请书 + + + +

中山間地域直接支払申请书 - {{ year }}年度

+ + {% for item in data %} +

{{ item.chusankan.c_id }} - {{ item.chusankan.oaza }}{{ item.chusankan.aza }}

+ +
+ 大字: + {{ item.chusankan.oaza }} +
+
+ 字: + {{ item.chusankan.aza }} +
+
+ 地番: + {{ item.chusankan.chiban }} +
+
+ 面積: + {{ item.chusankan.area }} ha +
+
+ 支払金額: + {{ item.chusankan.payment_amount|default:"-" }} 円 +
+
+ 関連圃場数: + {{ item.field_count }} +
+
+ 作付面積合計: + {{ item.total_area|floatformat:4 }} 反 +
+ + {% if item.crops %} + + + + + + + + + {% for crop in item.crops %} + + + + + {% endfor %} + +
作物品種
{{ crop.name }}{{ crop.variety }}
+ {% endif %} + + {% endfor %} + + diff --git a/backend/apps/reports/templates/reports/kyosai_template.html b/backend/apps/reports/templates/reports/kyosai_template.html new file mode 100644 index 0000000..eefb1bd --- /dev/null +++ b/backend/apps/reports/templates/reports/kyosai_template.html @@ -0,0 +1,96 @@ + + + + + 水稲共済申请书 + + + +

水稲共済申请书 - {{ year }}年度

+ + {% for item in data %} +

{{ item.kyosai.k_num }} - {{ item.kyosai.kanji_name }}

+ +
+ 住所: + {{ item.kyosai.address }} +
+
+ 面積: + {{ item.kyosai.area }} ha +
+
+ 関連圃場数: + {{ item.field_count }} +
+
+ 作付面積合計: + {{ item.total_area|floatformat:4 }} 反 +
+ + {% if item.crops %} + + + + + + + + + {% for crop in item.crops %} + + + + + {% endfor %} + +
作物品種
{{ crop.name }}{{ crop.variety }}
+ {% endif %} + + {% endfor %} + + diff --git a/backend/apps/reports/urls.py b/backend/apps/reports/urls.py new file mode 100644 index 0000000..c634742 --- /dev/null +++ b/backend/apps/reports/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from . import views + +urlpatterns = [ + path('kyosai//', views.generate_kyosai_pdf, name='kyosai_pdf'), + path('chusankan//', views.generate_chusankan_pdf, name='chusankan_pdf'), +] diff --git a/backend/apps/reports/views.py b/backend/apps/reports/views.py index 91ea44a..5ef7cbd 100644 --- a/backend/apps/reports/views.py +++ b/backend/apps/reports/views.py @@ -1,3 +1,88 @@ from django.shortcuts import render +from django.template.loader import render_to_string +from django.http import HttpResponse +from weasyprint import HTML +from apps.fields.models import OfficialKyosaiField, OfficialChusankanField, Field +from apps.plans.models import Plan -# Create your views here. + +def generate_kyosai_pdf(request, year): + kyosai_fields = OfficialKyosaiField.objects.all() + + data = [] + for kyosai in kyosai_fields: + related_fields = kyosai.fields.all() + plans = Plan.objects.filter(field__in=related_fields, year=year) + + crops = {} + total_area = 0 + for plan in plans: + crop_name = plan.crop.name + if crop_name not in crops: + crops[crop_name] = { + 'name': crop_name, + 'variety': plan.variety.name, + 'count': 0 + } + crops[crop_name]['count'] += 1 + total_area += float(plan.field.area_tan) + + data.append({ + 'kyosai': kyosai, + 'fields': related_fields, + 'crops': list(crops.values()), + 'total_area': total_area, + 'field_count': related_fields.count() + }) + + html_string = render_to_string('reports/kyosai_template.html', { + 'year': year, + 'data': data + }) + + pdf = HTML(string=html_string).write_pdf() + + response = HttpResponse(pdf, content_type='application/pdf') + response['Content-Disposition'] = f'attachment; filename="kyosai_{year}.pdf"' + return response + + +def generate_chusankan_pdf(request, year): + chusankan_fields = OfficialChusankanField.objects.all() + + data = [] + for chusankan in chusankan_fields: + related_fields = chusankan.fields.all() + plans = Plan.objects.filter(field__in=related_fields, year=year) + + crops = {} + total_area = 0 + for plan in plans: + crop_name = plan.crop.name + if crop_name not in crops: + crops[crop_name] = { + 'name': crop_name, + 'variety': plan.variety.name, + 'count': 0 + } + crops[crop_name]['count'] += 1 + total_area += float(plan.field.area_tan) + + data.append({ + 'chusankan': chusankan, + 'fields': related_fields, + 'crops': list(crops.values()), + 'total_area': total_area, + 'field_count': related_fields.count() + }) + + html_string = render_to_string('reports/chusankan_template.html', { + 'year': year, + 'data': data + }) + + pdf = HTML(string=html_string).write_pdf() + + response = HttpResponse(pdf, content_type='application/pdf') + response['Content-Disposition'] = f'attachment; filename="chusankan_{year}.pdf"' + return response diff --git a/backend/keinasystem/urls.py b/backend/keinasystem/urls.py index 0642ccb..59d7616 100644 --- a/backend/keinasystem/urls.py +++ b/backend/keinasystem/urls.py @@ -21,4 +21,5 @@ urlpatterns = [ path('admin/', admin.site.urls), path('api/fields/', include('apps.fields.urls')), path('api/plans/', include('apps.plans.urls')), + path('api/reports/', include('apps.reports.urls')), ]