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?
This commit is contained in:
@@ -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
|
||||
|
||||
108
backend/apps/reports/templates/reports/chusankan_template.html
Normal file
108
backend/apps/reports/templates/reports/chusankan_template.html
Normal file
@@ -0,0 +1,108 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>中山間地域直接支払申请书</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: "Noto Sans CJK JP", "Hiragino Kaku Gothic ProN", sans-serif;
|
||||
font-size: 10pt;
|
||||
line-height: 1.5;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-size: 14pt;
|
||||
margin-bottom: 20pt;
|
||||
}
|
||||
h2 {
|
||||
font-size: 12pt;
|
||||
border-bottom: 1px solid #333;
|
||||
margin-top: 15pt;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 10pt;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #333;
|
||||
padding: 5pt;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
th {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
.info-row {
|
||||
display: flex;
|
||||
margin-bottom: 5pt;
|
||||
}
|
||||
.info-label {
|
||||
font-weight: bold;
|
||||
width: 100pt;
|
||||
}
|
||||
.crop-table th {
|
||||
width: 30%;
|
||||
}
|
||||
.crop-table td {
|
||||
width: 70%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>中山間地域直接支払申请书 - {{ year }}年度</h1>
|
||||
|
||||
{% for item in data %}
|
||||
<h2>{{ item.chusankan.c_id }} - {{ item.chusankan.oaza }}{{ item.chusankan.aza }}</h2>
|
||||
|
||||
<div class="info-row">
|
||||
<span class="info-label">大字:</span>
|
||||
<span>{{ item.chusankan.oaza }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">字:</span>
|
||||
<span>{{ item.chusankan.aza }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">地番:</span>
|
||||
<span>{{ item.chusankan.chiban }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">面積:</span>
|
||||
<span>{{ item.chusankan.area }} ha</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">支払金額:</span>
|
||||
<span>{{ item.chusankan.payment_amount|default:"-" }} 円</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">関連圃場数:</span>
|
||||
<span>{{ item.field_count }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">作付面積合計:</span>
|
||||
<span>{{ item.total_area|floatformat:4 }} 反</span>
|
||||
</div>
|
||||
|
||||
{% if item.crops %}
|
||||
<table class="crop-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>作物</th>
|
||||
<th>品種</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for crop in item.crops %}
|
||||
<tr>
|
||||
<td>{{ crop.name }}</td>
|
||||
<td>{{ crop.variety }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
</body>
|
||||
</html>
|
||||
96
backend/apps/reports/templates/reports/kyosai_template.html
Normal file
96
backend/apps/reports/templates/reports/kyosai_template.html
Normal file
@@ -0,0 +1,96 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>水稲共済申请书</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: "Noto Sans CJK JP", "Hiragino Kaku Gothic ProN", sans-serif;
|
||||
font-size: 10pt;
|
||||
line-height: 1.5;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-size: 14pt;
|
||||
margin-bottom: 20pt;
|
||||
}
|
||||
h2 {
|
||||
font-size: 12pt;
|
||||
border-bottom: 1px solid #333;
|
||||
margin-top: 15pt;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 10pt;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #333;
|
||||
padding: 5pt;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
th {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
.info-row {
|
||||
display: flex;
|
||||
margin-bottom: 5pt;
|
||||
}
|
||||
.info-label {
|
||||
font-weight: bold;
|
||||
width: 100pt;
|
||||
}
|
||||
.crop-table th {
|
||||
width: 30%;
|
||||
}
|
||||
.crop-table td {
|
||||
width: 70%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>水稲共済申请书 - {{ year }}年度</h1>
|
||||
|
||||
{% for item in data %}
|
||||
<h2>{{ item.kyosai.k_num }} - {{ item.kyosai.kanji_name }}</h2>
|
||||
|
||||
<div class="info-row">
|
||||
<span class="info-label">住所:</span>
|
||||
<span>{{ item.kyosai.address }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">面積:</span>
|
||||
<span>{{ item.kyosai.area }} ha</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">関連圃場数:</span>
|
||||
<span>{{ item.field_count }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">作付面積合計:</span>
|
||||
<span>{{ item.total_area|floatformat:4 }} 反</span>
|
||||
</div>
|
||||
|
||||
{% if item.crops %}
|
||||
<table class="crop-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>作物</th>
|
||||
<th>品種</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for crop in item.crops %}
|
||||
<tr>
|
||||
<td>{{ crop.name }}</td>
|
||||
<td>{{ crop.variety }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
</body>
|
||||
</html>
|
||||
7
backend/apps/reports/urls.py
Normal file
7
backend/apps/reports/urls.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('kyosai/<int:year>/', views.generate_kyosai_pdf, name='kyosai_pdf'),
|
||||
path('chusankan/<int:year>/', views.generate_chusankan_pdf, name='chusankan_pdf'),
|
||||
]
|
||||
@@ -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
|
||||
|
||||
@@ -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')),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user