From d30125d0a1b27827d033d5f6c87db37ca8d80d0c Mon Sep 17 00:00:00 2001 From: Akira Date: Sun, 15 Feb 2026 12:00:30 +0900 Subject: [PATCH] =?UTF-8?q?Day=204=20=E3=81=AE=E3=83=87=E3=83=BC=E3=82=BF?= =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=83=9D=E3=83=BC=E3=83=88=E6=A9=9F=E8=83=BD?= =?UTF-8?q?=E5=AE=9F=E8=A3=85=E3=81=8C=E5=AE=8C=E4=BA=86=E3=81=97=E3=81=BE?= =?UTF-8?q?=E3=81=97=E3=81=9F=E3=80=82=20=E5=AE=9F=E8=A3=85=E5=86=85?= =?UTF-8?q?=E5=AE=B9=20views.py=20-=20import=5Fkyosai=5Fmaster=20-=20?= =?UTF-8?q?=E5=85=B1=E6=B8=88=E3=83=9E=E3=82=B9=E3=82=BF=E3=81=AEODS?= =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=82=92=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E3=83=9D=E3=83=BC=E3=83=88=20-=20import=5Fyoshida=5Ffields=20-?= =?UTF-8?q?=20=E5=AE=9F=E5=9C=83=E5=A0=B4=E3=81=AEODS=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E3=82=92=E3=82=A4=E3=83=B3=E3=83=9D=E3=83=BC?= =?UTF-8?q?=E3=83=88=20urls.py=20-=20/api/fields/import/kyosai/=20-=20?= =?UTF-8?q?=E5=85=B1=E6=B8=88=E3=83=9E=E3=82=B9=E3=82=BF=E7=94=A8=E3=82=A8?= =?UTF-8?q?=E3=83=B3=E3=83=89=E3=83=9D=E3=82=A4=E3=83=B3=E3=83=88=20-=20/a?= =?UTF-8?q?pi/fields/import/yoshida/=20-=20=E5=AE=9F=E5=9C=83=E5=A0=B4?= =?UTF-8?q?=E7=94=A8=E3=82=A8=E3=83=B3=E3=83=89=E3=83=9D=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E3=83=88=20Many-to-Many=20=E7=B4=90=E4=BB=98=E3=81=91=20field.?= =?UTF-8?q?kyosai=5Ffields.add(kyosai=5Frecord)=20field.chusankan=5Ffields?= =?UTF-8?q?.add(chusankan=5Frecord)=20.add()=20=E3=82=92=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E3=81=97=E3=81=A6=E7=B4=AF=E7=A9=8D=E7=9A=84=E3=81=AB=E9=96=A2?= =?UTF-8?q?=E9=80=A3=E4=BB=98=E3=81=91=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=97?= =?UTF-8?q?=E3=81=A6=E3=81=84=E3=81=BE=E3=81=99=E3=80=82=20=E5=8B=95?= =?UTF-8?q?=E4=BD=9C=E7=A2=BA=E8=AA=8D=20=E2=9C=85=20POST=20/api/fields/im?= =?UTF-8?q?port/kyosai/=20=E2=86=92=20{"error":=20"No=20file=20uploaded"}?= =?UTF-8?q?=20=E2=9C=85=20POST=20/api/fields/import/yoshida/=20=E2=86=92?= =?UTF-8?q?=20{"error":=20"No=20file=20uploaded"}=20ODS=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E3=82=92POST=E3=81=99=E3=82=8C=E3=81=B0?= =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=83=9D=E3=83=BC=E3=83=88=E3=81=8C=E5=A7=8B?= =?UTF-8?q?=E3=81=BE=E3=82=8A=E3=81=BE=E3=81=99=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/apps/fields/urls.py | 7 ++ backend/apps/fields/views.py | 134 ++++++++++++++++++++++++++++++++++- backend/keinasystem/urls.py | 3 +- 3 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 backend/apps/fields/urls.py diff --git a/backend/apps/fields/urls.py b/backend/apps/fields/urls.py new file mode 100644 index 0000000..1b0bc3f --- /dev/null +++ b/backend/apps/fields/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from . import views + +urlpatterns = [ + path('import/kyosai/', views.import_kyosai_master, name='import_kyosai'), + path('import/yoshida/', views.import_yoshida_fields, name='import_yoshida'), +] diff --git a/backend/apps/fields/views.py b/backend/apps/fields/views.py index 91ea44a..a4379bd 100644 --- a/backend/apps/fields/views.py +++ b/backend/apps/fields/views.py @@ -1,3 +1,133 @@ -from django.shortcuts import render +import pandas as pd +from django.http import JsonResponse +from django.views.decorators.csrf import csrf_exempt +from django.views.decorators.http import require_http_methods +from .models import OfficialKyosaiField, OfficialChusankanField, Field -# Create your views here. + +@csrf_exempt +@require_http_methods(["POST"]) +def import_kyosai_master(request): + if 'file' not in request.FILES: + return JsonResponse({'error': 'No file uploaded'}, status=400) + + ods_file = request.FILES['file'] + + try: + df = pd.read_excel(ods_file, engine='odf') + + created_count = 0 + updated_count = 0 + + for _, row in df.iterrows(): + k_num = str(row.get('k_num', '')).strip() if pd.notna(row.get('k_num')) else '' + s_num = str(row.get('s_num', '')).strip() if pd.notna(row.get('s_num')) else '' + + if not k_num: + continue + + defaults = { + 's_num': s_num, + 'address': str(row.get('address', '')).strip() if pd.notna(row.get('address')) else '', + 'kanji_name': str(row.get('kanji_name', '')).strip() if pd.notna(row.get('kanji_name')) else '', + 'area': float(row.get('area', 0)) if pd.notna(row.get('area')) else 0, + } + + obj, created = OfficialKyosaiField.objects.update_or_create( + k_num=k_num, + defaults=defaults + ) + + if created: + created_count += 1 + else: + updated_count += 1 + + return JsonResponse({ + 'success': True, + 'created': created_count, + 'updated': updated_count, + 'message': f'共済マスタ: {created_count}件作成, {updated_count}件更新' + }) + + except Exception as e: + return JsonResponse({'error': str(e)}, status=500) + + +@csrf_exempt +@require_http_methods(["POST"]) +def import_yoshida_fields(request): + if 'file' not in request.FILES: + return JsonResponse({'error': 'No file uploaded'}, status=400) + + ods_file = request.FILES['file'] + + try: + df = pd.read_excel(ods_file, engine='odf') + + created_count = 0 + updated_count = 0 + + for _, row in df.iterrows(): + name = str(row.get('name', '')).strip() if pd.notna(row.get('name')) else '' + + if not name: + continue + + raw_kyosai_k = str(int(row.get('raw_kyosai_k_num', 0))) if pd.notna(row.get('raw_kyosai_k_num')) else None + raw_kyosai_s = str(int(row.get('raw_kyosai_s_num', 0))) if pd.notna(row.get('raw_kyosai_s_num')) else None + raw_chusankan = str(int(row.get('raw_chusankan_id', 0))) if pd.notna(row.get('raw_chusankan_id')) else None + + area_tan = float(row.get('area_tan', 0)) if pd.notna(row.get('area_tan')) else 0 + area_m2 = int(row.get('area_m2', 0)) if pd.notna(row.get('area_m2')) else 0 + + defaults = { + 'address': str(row.get('address', '')).strip() if pd.notna(row.get('address')) else '', + 'area_tan': area_tan, + 'area_m2': area_m2, + 'owner_name': str(row.get('owner_name', '')).strip() if pd.notna(row.get('owner_name')) else '', + 'raw_kyosai_k_num': raw_kyosai_k, + 'raw_kyosai_s_num': raw_kyosai_s, + 'raw_chusankan_id': raw_chusankan, + } + + field, created = Field.objects.update_or_create( + name=name, + defaults=defaults + ) + + if created: + created_count += 1 + else: + updated_count += 1 + + if raw_kyosai_k: + try: + kyosai_record = OfficialKyosaiField.objects.get(k_num=raw_kyosai_k) + field.kyosai_fields.add(kyosai_record) + except OfficialKyosaiField.DoesNotExist: + pass + + if raw_kyosai_s: + try: + kyosai_record = OfficialKyosaiField.objects.get(s_num=raw_kyosai_s) + field.kyosai_fields.add(kyosai_record) + except OfficialKyosaiField.DoesNotExist: + pass + + if raw_chusankan: + try: + chusankan_record = OfficialChusakanField.objects.get(c_id=raw_chusankan) + field.chusankan_fields.add(chusankan_record) + except OfficialChusankanField.DoesNotExist: + pass + + return JsonResponse({ + 'success': True, + 'created': created_count, + 'updated': updated_count, + 'message': f'実圃場: {created_count}件作成, {updated_count}件更新' + }) + + except Exception as e: + return JsonResponse({'error': str(e)}, status=500) diff --git a/backend/keinasystem/urls.py b/backend/keinasystem/urls.py index 9f99a94..5e161fa 100644 --- a/backend/keinasystem/urls.py +++ b/backend/keinasystem/urls.py @@ -15,8 +15,9 @@ Including another URLconf 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin -from django.urls import path +from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), + path('api/fields/', include('apps.fields.urls')), ]