実装内容: 1. frontend/src/types/index.ts - 型定義(Field, Crop, Variety, Plan) 2. frontend/src/components/Navbar.tsx - ナビゲーションバー(ログアウトボタン) 3. backend/apps/fields/views.py - FieldViewSet追加 4. backend/apps/fields/serializers.py - 新規作成(Fieldシリアライザー) 5. backend/apps/fields/urls.py - ViewSetルート追加 6. frontend/src/app/allocation/page.tsx - 作付け計画画面(作物・品種選択可能) 7. frontend/src/app/page.tsx - 自動リダイレクト(ログイン状態による) API動作確認: - /api/fields/ → HTTP 200(圃場データなし) - /api/plans/crops/ → HTTP 200(2作物:水稲・大豆) - /api/plans/?year=2025 → HTTP 200 テスト: http://localhost:3000/ → 自動リダイレクトで /login または /allocation ※ 現在圃場データがないため、画面には「圃場データがありません。インポートを実行してください。」と表示されます。 次の工程に移りますか?
143 lines
5.0 KiB
Python
143 lines
5.0 KiB
Python
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 rest_framework import viewsets, permissions
|
|
from rest_framework.decorators import action
|
|
from .models import OfficialKyosaiField, OfficialChusankanField, Field
|
|
from .serializers import FieldSerializer
|
|
|
|
|
|
class FieldViewSet(viewsets.ReadOnlyModelViewSet):
|
|
queryset = Field.objects.all()
|
|
serializer_class = FieldSerializer
|
|
permission_classes = [permissions.AllowAny]
|
|
|
|
|
|
@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 = OfficialChusankanField.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)
|