2024-08-09 23:49:36 +00:00
from django . core . exceptions import ValidationError as DjangoValidationError
2024-08-02 07:01:32 +00:00
from . models import JpnSubPerf # このインポート文をファイルの先頭に追加
from django . contrib . auth import get_user_model
User = get_user_model ( )
import traceback
2024-08-02 14:21:50 +00:00
from django . contrib . auth . hashers import make_password
2025-08-24 19:44:36 +09:00
from urllib . parse import unquote # URLデコード用
2024-08-02 14:21:50 +00:00
2024-11-08 04:30:58 +00:00
import subprocess # subprocessモジュールを追加
import tempfile # tempfileモジュールを追加
import shutil # shutilモジュールを追加
2024-08-02 14:21:50 +00:00
from django . contrib . auth . tokens import default_token_generator
from django . utils . http import urlsafe_base64_encode , urlsafe_base64_decode
from django . utils . encoding import force_bytes , force_str
2024-08-02 07:01:32 +00:00
2024-08-01 07:51:52 +00:00
import requests
2024-07-25 00:57:48 +00:00
from rest_framework import serializers
from django . db import IntegrityError
from django . urls import reverse
2025-09-03 21:56:40 +09:00
# rog.utilsディレクトリではなく、rog/utils.pyファイルからインポート
import rog . utils as rog_utils_module
2024-07-26 12:34:54 +00:00
from django . conf import settings
2024-07-25 00:57:48 +00:00
import uuid
from rest_framework . exceptions import ValidationError as DRFValidationError
from django . db import transaction
2025-08-30 04:12:55 +09:00
from django . db . models import F , Sum , Q
2024-07-25 00:57:48 +00:00
from rest_framework import viewsets , permissions , status
from rest_framework . decorators import action
from rest_framework . response import Response
from django . shortcuts import get_object_or_404
from . models import Team , Member , CustomUser , NewCategory
2024-07-26 12:34:54 +00:00
from . serializers import TeamSerializer , MemberSerializer , CustomUserSerializer , TeamDetailSerializer , UserUpdateSerializer , UserRegistrationSerializer
2024-08-26 09:02:01 +00:00
from . permissions import IsTeamOwner , IsTeamOwnerOrMember
2024-07-25 00:57:48 +00:00
2022-10-06 17:42:12 +05:30
from curses . ascii import NUL
2022-03-14 10:50:11 +05:30
from django . core . serializers import serialize
2025-08-30 02:20:25 +09:00
from . models import GoalImages , Location2025 , Location_line , Location_polygon , JpnAdminMainPerf , Useractions , GifuAreas , RogUser , CustomUser , UserTracks , GoalImages , CheckinImages , NewEvent , NewEvent2 , Team , Category , NewCategory , Entry , Member , TempUser , EntryMember , GpsCheckin
2022-03-14 10:50:11 +05:30
from rest_framework import viewsets
2024-08-02 14:21:50 +00:00
from . serializers import LocationSerializer , Location_lineSerializer , Location_polygonSerializer , JPN_main_perfSerializer , LocationCatSerializer , UserSerializer , LoginUserSerializer , UseractionsSerializer , UserDestinationSerializer , GifuAreaSerializer , LocationEventNameSerializer , RogUserSerializer , UserTracksSerializer , ChangePasswordSerializer , GolaImageSerializer , CheckinImageSerializer , RegistrationSerializer , MemberWithUserSerializer , TempUserRegistrationSerializer , PasswordResetRequestSerializer , PasswordResetConfirmSerializer
2022-04-27 15:47:37 +05:30
from knox . models import AuthToken
2022-03-16 17:46:22 +05:30
2022-10-18 14:58:20 +05:30
from rest_framework import viewsets , generics , status
2022-03-14 10:50:11 +05:30
from rest_framework . response import Response
from rest_framework . parsers import JSONParser , MultiPartParser
2022-03-23 09:21:17 +05:30
from . serializers import LocationSerializer
2022-03-30 16:17:48 +05:30
from django . http import JsonResponse
2024-11-12 09:09:00 +09:00
from rest_framework . permissions import IsAuthenticated , AllowAny
#
# AllowAny: 認証なしで誰でもアクセス可能
# IsAuthenticated: 認証済みユーザーのみアクセス可能
# IsAdminUser: 管理者のみアクセス可能
# IsAuthenticatedOrReadOnly: 読み取りは誰でも可能、書き込みは認証済みユーザーのみ可能
2022-12-27 18:01:18 +05:30
from django . contrib . gis . db . models import Extent , Union
2022-03-14 10:50:11 +05:30
2024-07-26 04:03:15 +00:00
from . serializers import TestSerialiser , NewEventSerializer , NewEvent2Serializer , TeamSerializer , NewCategorySerializer , CategorySerializer , EntrySerializer , MemberSerializer , TempUserSerializer , CustomUserSerializer , EntryMemberSerializer , MemberCreationSerializer , EntryCreationSerializer
2022-05-12 02:15:36 +05:30
from . models import TestModel
from django . shortcuts import get_object_or_404
2024-08-09 23:49:36 +00:00
from django . db . models import Max
2022-05-16 18:36:38 +05:30
from django . contrib . gis import geos
2022-06-06 21:15:07 +05:30
from django . db . models import Q
2022-05-12 02:15:36 +05:30
2022-10-18 14:58:20 +05:30
from rest_framework import permissions
from rest_framework . views import APIView
from rest_framework . decorators import api_view , permission_classes
from rest_framework . parsers import JSONParser , MultiPartParser
from django . views . decorators . csrf import csrf_exempt
2023-01-09 17:11:03 +05:30
from django . shortcuts import render
2024-07-24 00:38:32 +00:00
from . permissions import IsMemberOrTeamOwner
2022-10-18 14:58:20 +05:30
2024-07-24 00:38:32 +00:00
from django . utils . decorators import method_decorator
from django . utils . encoding import force_str
2022-03-14 10:50:11 +05:30
2024-07-25 00:57:48 +00:00
import logging
2024-10-28 20:25:05 +00:00
from datetime import datetime , timedelta
2025-08-31 12:08:36 +09:00
from django . core . cache import cache
import time
2024-07-26 12:34:54 +00:00
2024-08-09 23:49:36 +00:00
from django . contrib . gis . measure import D
from django . contrib . gis . geos import Point
from django . contrib . gis . db . models . functions import Distance
2024-07-26 12:34:54 +00:00
from django . utils . dateparse import parse_date
2024-09-03 13:19:30 +00:00
from django . utils import timezone
import csv
import io
from django . contrib import admin
from django . shortcuts import render
from django . http import HttpResponseRedirect
from django . urls import path
from django import forms
from . models import NewEvent2 , CustomUser , Team , NewCategory , Entry , Member , TempUser
from django . core . mail import send_mail
from django . conf import settings
from django . db import transaction
from django . core . exceptions import ValidationError
2024-07-25 00:57:48 +00:00
2024-10-28 02:20:28 +00:00
import xlsxwriter
from io import BytesIO
2024-10-28 20:25:05 +00:00
from django . urls import get_resolver
2024-10-29 00:56:12 +00:00
import os
2024-11-02 23:53:34 +00:00
import json
2024-11-08 04:30:58 +00:00
from django . http import HttpResponse
2024-11-07 03:24:15 +09:00
from sumaexcel import SumasenExcel
2024-07-25 00:57:48 +00:00
logger = logging . getLogger ( __name__ )
2024-10-21 12:48:42 +00:00
@api_view ( [ ' PATCH ' ] )
@permission_classes ( [ IsAuthenticated ] )
def update_entry_status ( request , entry_id ) :
try :
entry = Entry . objects . get ( id = entry_id )
except Entry . DoesNotExist :
return Response ( { " error " : " Entry not found " } , status = status . HTTP_404_NOT_FOUND )
# エントリーの所有者またはチームのメンバーのみが更新可能
if entry . owner != request . user and not entry . team . members . filter ( user = request . user ) . exists ( ) :
return Response ( { " error " : " You don ' t have permission to update this entry " } , status = status . HTTP_403_FORBIDDEN )
2025-09-02 20:06:58 +09:00
# フィールド名の両方のパターンを受け入れる
hasParticipated = request . data . get ( ' hasParticipated ' ) or request . data . get ( ' has_participated ' )
hasGoaled = request . data . get ( ' hasGoaled ' ) or request . data . get ( ' has_goaled ' )
2024-10-21 12:48:42 +00:00
if hasParticipated is not None :
entry . hasParticipated = hasParticipated
if hasGoaled is not None :
entry . hasGoaled = hasGoaled
2025-09-02 20:06:58 +09:00
# update()を使用してバリデーションをスキップ
Entry . objects . filter ( id = entry_id ) . update (
hasParticipated = entry . hasParticipated ,
hasGoaled = entry . hasGoaled
)
# 更新後のオブジェクトを再取得
entry . refresh_from_db ( )
2024-10-21 12:48:42 +00:00
serializer = EntrySerializer ( entry )
return Response ( serializer . data )
2024-09-03 13:19:30 +00:00
def process_csv_upload ( csv_file , event ) :
decoded_file = csv_file . read ( ) . decode ( ' utf-8 ' )
io_string = io . StringIO ( decoded_file )
reader = csv . reader ( io_string )
next ( reader ) # ヘッダーをスキップ
for row in reader :
try :
owner_email , owner_name , owner_grade , owner_gender , team_name , category_name , * member_data = row
# ここでデータを処理し、必要なオブジェクトを作成します
# 例: create_or_update_team(owner_email, owner_name, owner_grade, owner_gender, team_name, category_name, event)
# 例: process_members(member_data, team)
except ValidationError as e :
# エラーハンドリング
pass
class CSVUploadForm ( forms . Form ) :
event = forms . ModelChoiceField ( queryset = NewEvent2 . objects . all ( ) )
csv_file = forms . FileField ( )
class NewEvent2Admin ( admin . ModelAdmin ) :
2024-10-21 12:48:42 +00:00
list_display = [ ' event_name ' , ' start_datetime ' , ' end_datetime ' , ' deadlineDateTime ' , ' public ' ]
2024-09-03 13:19:30 +00:00
def get_urls ( self ) :
urls = super ( ) . get_urls ( )
my_urls = [
path ( ' csv-upload/ ' , self . admin_site . admin_view ( self . csv_upload_view ) , name = ' csv-upload ' ) ,
]
return my_urls + urls
def csv_upload_view ( self , request ) :
if request . method == ' POST ' :
form = CSVUploadForm ( request . POST , request . FILES )
if form . is_valid ( ) :
event = form . cleaned_data [ ' event ' ]
csv_file = request . FILES [ ' csv_file ' ]
csv_data = csv_file . read ( ) . decode ( ' utf-8 ' )
csv_reader = csv . reader ( io . StringIO ( csv_data ) )
next ( csv_reader ) # ヘッダーをスキップ
for row in csv_reader :
self . process_team ( event , row )
self . message_user ( request , " CSV file has been processed successfully. " )
return HttpResponseRedirect ( " .. " )
else :
form = CSVUploadForm ( )
context = {
' form ' : form ,
' title ' : ' Upload CSV file ' ,
}
return render ( request , ' admin/csv_upload.html ' , context )
@transaction.atomic
def process_team ( self , event , row ) :
owner_data = row [ : 4 ]
team_name = row [ 4 ]
category_name = row [ 5 ]
members_data = row [ 6 : ]
owner = self . create_or_get_user ( * owner_data )
category = NewCategory . objects . get ( category_name = category_name )
team , _ = Team . objects . get_or_create ( owner = owner , team_name = team_name , category = category )
entry , _ = Entry . objects . get_or_create (
team = team ,
event = event ,
category = category ,
owner = owner ,
date = event . start_datetime . date ( )
)
for i in range ( 0 , len ( members_data ) , 4 ) :
member_data = members_data [ i : i + 4 ]
if member_data [ 0 ] : # メールアドレスがある場合のみ処理
member = self . create_or_get_user ( * member_data )
Member . objects . get_or_create ( team = team , user = member )
def create_or_get_user ( self , email , name , grade , gender ) :
is_dummy = False
if not email :
email = f " dummy_ { uuid . uuid4 ( ) } @example.com "
is_dummy = True
user , created = CustomUser . objects . get_or_create ( email = email )
if created or is_dummy :
user . firstname , user . lastname = name . split ( ' ' , 1 ) if ' ' in name else ( name , ' ' )
user . grade = grade
user . gender = gender
user . is_active = not is_dummy
user . save ( )
if not is_dummy :
self . send_invitation_email ( user )
return user
def send_invitation_email ( self , user ) :
subject = ' Invitation to join the event '
message = f ' Please click the following link to activate your account: { settings . SITE_URL } /activate/ { user . id } / '
send_mail ( subject , message , settings . DEFAULT_FROM_EMAIL , [ user . email ] )
2024-07-25 00:57:48 +00:00
2022-03-14 10:50:11 +05:30
class LocationViewSet ( viewsets . ModelViewSet ) :
2025-08-30 02:20:25 +09:00
queryset = Location2025 . objects . all ( )
2022-03-14 10:50:11 +05:30
serializer_class = LocationSerializer
2025-08-30 04:12:55 +09:00
filter_fields = [
" event " , " cp_name " , " subcategory " , " sub_loc_id " ,
" is_active " , " hidden_location " , " evaluation_value "
]
2022-03-14 10:50:11 +05:30
2024-11-02 23:53:34 +00:00
def get_queryset ( self ) :
2025-08-30 02:20:25 +09:00
queryset = Location2025 . objects . all ( )
2024-11-02 23:53:34 +00:00
# リクエストパラメータの確認
2025-08-30 04:12:55 +09:00
event_filter = self . request . query_params . get ( ' event ' )
subcategory_filter = self . request . query_params . get ( ' subcategory ' )
has_photos_filter = self . request . query_params . get ( ' has_photos ' )
has_videos_filter = self . request . query_params . get ( ' has_videos ' )
tags_filter = self . request . query_params . get ( ' tags__contains ' )
evaluation_filter = self . request . query_params . get ( ' evaluation_value ' )
hidden_filter = self . request . query_params . get ( ' hidden_location ' )
if event_filter :
queryset = queryset . filter ( event_id = event_filter )
if subcategory_filter :
queryset = queryset . filter ( subcategory__icontains = subcategory_filter )
if has_photos_filter == ' true ' :
queryset = queryset . exclude ( photos__isnull = True ) . exclude ( photos = ' ' )
elif has_photos_filter == ' false ' :
queryset = queryset . filter ( Q ( photos__isnull = True ) | Q ( photos = ' ' ) )
if has_videos_filter == ' true ' :
queryset = queryset . exclude ( videos__isnull = True ) . exclude ( videos = ' ' )
elif has_videos_filter == ' false ' :
queryset = queryset . filter ( Q ( videos__isnull = True ) | Q ( videos = ' ' ) )
if tags_filter :
queryset = queryset . filter ( tags__icontains = tags_filter )
if evaluation_filter :
queryset = queryset . filter ( evaluation_value = evaluation_filter )
if hidden_filter == ' true ' :
queryset = queryset . filter ( hidden_location = True )
elif hidden_filter == ' false ' :
queryset = queryset . filter ( hidden_location = False )
2024-11-02 23:53:34 +00:00
return queryset
def list ( self , request , * args , * * kwargs ) :
try :
response = super ( ) . list ( request , * args , * * kwargs )
return response
except Exception as e :
logger . error ( f " Error in list method: { str ( e ) } " , exc_info = True )
raise
2022-03-14 10:50:11 +05:30
class Location_lineViewSet ( viewsets . ModelViewSet ) :
queryset = Location_line . objects . all ( )
serializer_class = Location_lineSerializer
class Location_polygonViewSet ( viewsets . ModelViewSet ) :
queryset = Location_polygon . objects . all ( )
serializer_class = Location_polygonSerializer
2022-03-16 17:46:22 +05:30
class Jpn_Main_PerfViewSet ( viewsets . ModelViewSet ) :
2022-06-08 21:40:43 +05:30
queryset = JpnAdminMainPerf . objects . filter ( id = 9 )
2022-03-16 17:46:22 +05:30
serializer_class = JPN_main_perfSerializer
2022-03-23 09:21:17 +05:30
filter_fields = [ " adm1_ja " ]
2022-03-14 10:50:11 +05:30
2023-05-26 14:54:43 +05:30
2022-03-14 10:50:11 +05:30
2022-06-13 01:55:45 +05:30
class UserTracksViewSet ( viewsets . ModelViewSet ) :
queryset = UserTracks . objects . all ( )
serializer_class = UserTracksSerializer
2024-07-31 00:56:23 +00:00
@api_view ( [ ' PUT ' ] )
@permission_classes ( [ IsAuthenticated ] )
def update_user_info ( request , user_id ) :
try :
user = CustomUser . objects . get ( id = user_id )
except CustomUser . DoesNotExist :
return Response ( { " error " : " User not found " } , status = status . HTTP_404_NOT_FOUND )
if request . user . id != user_id :
return Response ( { " error " : " You don ' t have permission to update this user ' s information " } , status = status . HTTP_403_FORBIDDEN )
data = request . data
logger . debug ( f " Received data for update: { data } " )
# CustomUserの更新可能なフィールドを指定
updateable_fields = [ ' zekken_number ' , ' event_code ' , ' team_name ' , ' group ' ]
#for field in updateable_fields:
# if field in data:
# setattr(user, field, data[field])
updated_fields = [ ]
for field in updateable_fields :
if field in data :
old_value = getattr ( user , field )
setattr ( user , field , data [ field ] )
new_value = getattr ( user , field )
if old_value != new_value :
updated_fields . append ( f " { field } : { old_value } -> { new_value } " )
logger . debug ( f " Fields to be updated: { updated_fields } " )
try :
user . save ( )
logger . info ( f " User { user_id } updated. Changed fields: { ' , ' . join ( updated_fields ) } " )
serializer = CustomUserSerializer ( user )
return Response ( serializer . data , status = status . HTTP_200_OK )
except Exception as e :
logger . error ( f " Error updating user { user_id } : { str ( e ) } " )
return Response ( { " error " : str ( e ) } , status = status . HTTP_400_BAD_REQUEST )
@api_view ( [ ' PUT ' ] )
@permission_classes ( [ IsAuthenticated ] )
def update_user_detail ( request , user_id ) :
try :
user = CustomUser . objects . get ( id = user_id )
except CustomUser . DoesNotExist :
return Response ( { " error " : " User not found " } , status = status . HTTP_404_NOT_FOUND )
if request . user . id != user_id :
return Response ( { " error " : " You don ' t have permission to update this user ' s information " } , status = status . HTTP_403_FORBIDDEN )
data = request . data
logger . debug ( f " Received data for update: { data } " )
# CustomUserの更新可能なフィールドを指定
updateable_fields = [ ' firstname ' , ' lastname ' , ' date_of_birth ' , ' female ' ]
#for field in updateable_fields:
# if field in data:
# setattr(user, field, data[field])
updated_fields = [ ]
for field in updateable_fields :
if field in data :
old_value = getattr ( user , field )
setattr ( user , field , data [ field ] )
new_value = getattr ( user , field )
if old_value != new_value :
2024-08-01 07:51:52 +00:00
logger . debug ( f " { field } : { old_value } -> { new_value } " )
2024-07-31 00:56:23 +00:00
updated_fields . append ( f " { field } : { old_value } -> { new_value } " )
logger . debug ( f " Fields to be updated: { updated_fields } " )
try :
user . save ( )
logger . info ( f " User { user_id } updated. Changed fields: { ' , ' . join ( updated_fields ) } " )
2024-08-01 07:51:52 +00:00
# 更新されたUserデータを使用して関連するMemberのデータを更新
members = Member . objects . filter ( user = user )
for member in members :
member . is_temporary = False
member . save ( )
2024-07-31 00:56:23 +00:00
serializer = CustomUserSerializer ( user )
return Response ( serializer . data , status = status . HTTP_200_OK )
except Exception as e :
logger . error ( f " Error updating user { user_id } : { str ( e ) } " )
return Response ( { " error " : str ( e ) } , status = status . HTTP_400_BAD_REQUEST )
2022-03-14 10:50:11 +05:30
2022-03-30 16:17:48 +05:30
def LocationsInPerf ( request ) :
perfecture = request . GET . get ( ' perf ' )
2022-08-23 19:08:56 +05:30
is_rog = request . GET . get ( ' rog ' )
2022-04-27 15:47:37 +05:30
cat = request . GET . get ( ' cat ' )
2022-09-05 19:10:39 +05:30
grp = request . GET . get ( ' grp ' )
2022-03-30 16:17:48 +05:30
perf_geom = JpnAdminMainPerf . objects . get ( id = perfecture )
2022-04-27 15:47:37 +05:30
if ( cat ) :
2022-08-23 19:08:56 +05:30
if is_rog :
2022-09-05 19:10:39 +05:30
if grp :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( ~ Q ( cp = 0 ) , geom__within = perf_geom . geom , category = cat , group__contains = grp )
2022-09-05 19:10:39 +05:30
else :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( ~ Q ( cp = 0 ) , geom__within = perf_geom . geom , category = cat )
2022-08-23 19:08:56 +05:30
else :
2022-09-05 19:10:39 +05:30
if grp :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( geom__within = perf_geom . geom , category = cat , group__contains = grp , location_name__contains = ' 観光 ' )
2022-09-05 19:10:39 +05:30
else :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( geom__within = perf_geom . geom , category = cat , location_name__contains = ' 観光 ' )
2022-04-27 15:47:37 +05:30
else :
2022-08-23 19:08:56 +05:30
if is_rog :
2022-09-05 19:10:39 +05:30
if grp :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( ~ Q ( cp = 0 ) , geom__within = perf_geom . geom , group__contains = grp )
2022-09-05 19:10:39 +05:30
else :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( ~ Q ( cp = 0 ) , geom__within = perf_geom . geom )
2022-08-23 19:08:56 +05:30
else :
2022-09-05 19:10:39 +05:30
if grp :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( geom__within = perf_geom . geom , group__contains = grp , location_name__contains = ' 観光 ' )
2022-09-05 19:10:39 +05:30
else :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( geom__within = perf_geom . geom , location_name__contains = ' 観光 ' )
2022-04-27 15:47:37 +05:30
2022-03-30 16:17:48 +05:30
serializer = LocationSerializer ( locs , many = True )
return JsonResponse ( serializer . data , safe = False )
2022-04-18 11:33:49 +05:30
def LocationsInSubPerf ( request ) :
subperfecture = request . GET . get ( ' subperf ' )
2022-08-23 19:08:56 +05:30
is_rog = request . GET . get ( ' rog ' )
2022-04-27 15:47:37 +05:30
cat = request . GET . get ( ' cat ' )
2022-09-05 19:10:39 +05:30
grp = request . GET . get ( ' grp ' )
2022-04-18 11:33:49 +05:30
perf_geom = JpnSubPerf . objects . get ( id = subperfecture )
2022-04-27 15:47:37 +05:30
if ( cat ) :
2022-08-23 19:08:56 +05:30
if is_rog :
2022-09-05 19:10:39 +05:30
if grp :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( ~ Q ( cp = 0 ) , geom__within = perf_geom . geom , category = cat , group__contains = grp )
2022-09-05 19:10:39 +05:30
else :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( ~ Q ( cp = 0 ) , geom__within = perf_geom . geom , category = cat )
2022-08-23 19:08:56 +05:30
else :
2022-09-05 19:10:39 +05:30
if grp :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( geom__within = perf_geom . geom , category = cat , group__contains = grp , location_name__contains = ' 観光 ' )
2022-09-05 19:10:39 +05:30
else :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( geom__within = perf_geom . geom , category = cat , location_name__contains = ' 観光 ' )
2022-04-27 15:47:37 +05:30
else :
2022-08-23 19:08:56 +05:30
if is_rog :
2022-09-05 19:10:39 +05:30
if grp :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( ~ Q ( cp = 0 ) , geom__within = perf_geom . geom , group__contains = grp )
2022-09-05 19:10:39 +05:30
else :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( ~ Q ( cp = 0 ) , geom__within = perf_geom . geom )
2022-08-23 19:08:56 +05:30
else :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( geom__within = perf_geom . geom , location_name__contains = ' 観光 ' )
2022-04-18 11:33:49 +05:30
serializer = LocationSerializer ( locs , many = True )
return JsonResponse ( serializer . data , safe = False )
2024-07-31 00:56:23 +00:00
# この関数LocationInBoundは、地理的な範囲内にある特定の条件を満たす位置情報を検索し、結果をJSON形式で返すものです。主なロジックは以下の通りです:
#
# 1.リクエストパラメータの取得:
# 4つの緯度経度ペア( lat1/lon1からlat4/lon4)
# カテゴリ( cat)
# グループ( grp)
# ROG( Region of Gaze) フラグ( is_rog)
#
# 2. 境界ポリゴンの作成:
# 4つの緯度経度ペアが全て提供された場合、それらを使用してジオメトリポリゴンを作成します。
#
# 3.位置情報のフィルタリング:
# 基本的に、ポリゴン内( geom__within=pl) にある位置情報を検索します。
# イベント名がない( event_name__isnull=True) 位置情報のみを対象とします。
# カテゴリ、グループ、ROGフラグの有無に応じて、さらにフィルタリングを行います:
# カテゴリが指定された場合、そのカテゴリに一致する位置情報のみを検索します。
# ROGフラグがある場合、cp( おそらくcheck point) が0でない位置情報を検索します。
# ROGフラグがない場合、location_nameに'観光'を含む位置情報のみを検索します。
# グループが指定された場合、そのグループを含む位置情報のみを検索します。
#
# 4.結果の返却:
# 検索結果が120件を超える場合、"too_many_points"フラグを立てたJSONレスポンスを返します。
# それ以外の場合、LocationSerializerを使用して位置情報をシリアライズし、JSONレスポンスとして返します。
#
# 5.エラーハンドリング:
# 必要な緯度経度パラメータが不足している場合、空のJSONオブジェクトを返します。
#
# この関数は、主に観光関連の位置情報を特定の地理的範囲内で検索し、様々な条件でフィルタリングすることができます。ROGフラグの有無によって検索条件が変わるのが特徴的です。
#
2024-08-09 23:49:36 +00:00
2023-05-26 14:54:43 +05:30
def LocationInBound ( request ) :
2024-07-31 00:56:23 +00:00
logger . debug ( f " Received request parameters: { request . GET } " )
2024-08-09 23:49:36 +00:00
lat = float ( request . GET . get ( ' la1 ' ) )
lon = float ( request . GET . get ( ' ln1 ' ) )
cat = request . GET . get ( ' cat ' )
grp = request . GET . get ( ' grp ' )
is_rog = request . GET . get ( ' rog ' )
logger . debug ( f " Parsed parameters: lat= { lat } , lon= { lon } , "
f " cat= { cat } , grp= { grp } , is_rog= { is_rog } " )
if lat is not None and lon is not None :
current_point = Point ( lon , lat , srid = 4326 )
# 10km四方の領域を指定
distance_from_point = 40000 # 中心から10km( 半径)
2025-08-30 02:20:25 +09:00
base_query = Location2025 . objects . filter (
2024-08-09 23:49:36 +00:00
geom__distance_lte = ( current_point , D ( m = distance_from_point ) )
) . annotate ( distance = Distance ( ' geom ' , current_point ) ) . order_by ( ' distance ' )
#if cat:
# base_query = base_query.filter(category=cat)
#if is_rog:
#base_query = base_query.filter(~Q(cp=0))
#else:
# base_query = base_query.filter(location_name__contains='観光')
if grp :
base_query = base_query . filter ( group__contains = grp )
logger . debug ( f " Final query: { base_query . query } " )
locs = base_query
logger . debug ( f " Number of locations found: { len ( locs ) } " )
if len ( locs ) > 200 :
return JsonResponse ( { " too_many_points " : True } , safe = False , status = 500 )
else :
serializer = LocationSerializer ( locs , many = True )
return JsonResponse ( serializer . data , safe = False )
else :
return JsonResponse ( { " error " : " Latitude and longitude are required " } , safe = False , status = 400 )
def LocationInBound2 ( request ) :
logger . debug ( f " Received request parameters: { request . GET } " )
lat = float ( request . GET . get ( ' la1 ' ) )
lon = float ( request . GET . get ( ' ln1 ' ) )
cat = request . GET . get ( ' cat ' )
grp = request . GET . get ( ' grp ' )
is_rog = request . GET . get ( ' rog ' )
logger . debug ( f " Parsed parameters: lat= { lat } , lon= { lon } , "
f " cat= { cat } , grp= { grp } , is_rog= { is_rog } " )
if lat is not None and lon is not None :
if grp :
2025-08-31 20:05:27 +09:00
# grpがある場合、イベント名に基づいてフィルタリング
from rog . models import NewEvent2
from django . db . models import Q
# grpパラメータでイベントを検索
events = NewEvent2 . objects . filter (
Q ( event_code = grp ) |
Q ( event_name__icontains = grp )
)
if events . exists ( ) :
base_query = Location2025 . objects . filter ( event__in = events )
else :
# イベントが見つからない場合は空のクエリセット
base_query = Location2025 . objects . none ( )
2024-08-09 23:49:36 +00:00
else :
current_point = Point ( lon , lat , srid = 4326 )
# 10km四方の領域を指定
distance_from_point = 40000 # 中心から10km( 半径)
2025-08-30 02:20:25 +09:00
base_query = Location2025 . objects . filter (
2025-08-31 20:05:27 +09:00
location__distance_lte = ( current_point , D ( m = distance_from_point ) )
) . annotate ( distance = Distance ( ' location ' , current_point ) ) . order_by ( ' distance ' )
2024-08-09 23:49:36 +00:00
logger . debug ( f " Final query: { base_query . query } " )
locs = base_query
logger . debug ( f " Number of locations found: { len ( locs ) } " )
if len ( locs ) > 200 :
return JsonResponse ( { " too_many_points " : True } , safe = False , status = 500 )
else :
serializer = LocationSerializer ( locs , many = True )
return JsonResponse ( serializer . data , safe = False )
else :
return JsonResponse ( { " error " : " Latitude and longitude are required " } , safe = False , status = 400 )
def LocationInBound_orig ( request ) :
logger . debug ( f " Received request parameters: { request . GET } " )
2023-05-26 14:54:43 +05:30
lat1 = float ( request . GET . get ( ' la1 ' ) )
lon1 = float ( request . GET . get ( ' ln1 ' ) )
lat2 = float ( request . GET . get ( ' la2 ' ) )
lon2 = float ( request . GET . get ( ' ln2 ' ) )
lat3 = float ( request . GET . get ( ' la3 ' ) )
lon3 = float ( request . GET . get ( ' ln3 ' ) )
lat4 = float ( request . GET . get ( ' la4 ' ) )
lon4 = float ( request . GET . get ( ' ln4 ' ) )
cat = request . GET . get ( ' cat ' )
grp = request . GET . get ( ' grp ' )
2023-05-26 13:56:26 +05:30
2023-05-26 14:54:43 +05:30
is_rog = request . GET . get ( ' rog ' )
2022-05-17 19:11:52 +05:30
2024-07-31 00:56:23 +00:00
logger . debug ( f " Parsed parameters: lat1= { lat1 } , lon1= { lon1 } , lat2= { lat2 } , lon2= { lon2 } , "
f " lat3= { lat3 } , lon3= { lon3 } , lat4= { lat4 } , lon4= { lon4 } , "
f " cat= { cat } , grp= { grp } , is_rog= { is_rog } " )
2024-08-09 23:49:36 +00:00
print ( f " ===== cat= { cat } , grp= { grp } , is_rog= { is_rog } ======= " )
2023-05-26 14:54:43 +05:30
if ( lat1 != None and lon1 != None and lat2 != None and lon2 != None and lat3 != None and lon3 != None and lat4 != None and lon4 != None ) :
pl = geos . Polygon ( ( ( lon1 , lat1 ) , ( lon2 , lat2 ) , ( lon3 , lat3 ) , ( lon4 , lat4 ) , ( lon1 , lat1 ) ) , srid = 4326 )
2024-07-31 00:56:23 +00:00
logger . debug ( f " Created polygon: { pl } " )
2025-08-30 02:20:25 +09:00
base_query = Location2025 . objects . filter ( geom__within = pl , event_name__isnull = True )
2024-07-31 00:56:23 +00:00
if cat :
base_query = base_query . filter ( category = cat )
if is_rog :
base_query = base_query . filter ( ~ Q ( cp = 0 ) )
else :
base_query = base_query . filter ( location_name__contains = ' 観光 ' )
if grp :
base_query = base_query . filter ( group__contains = grp )
logger . debug ( f " Final query: { base_query . query } " )
locs = base_query
logger . debug ( f " Number of locations found: { len ( locs ) } " )
'''
2023-05-26 14:54:43 +05:30
if ( cat ) :
2022-08-26 18:31:54 +05:30
if is_rog :
2023-05-26 14:54:43 +05:30
if grp :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( ~ Q ( cp = 0 ) , geom__within = pl , category = cat , event_name__isnull = True , group__contains = grp )
2023-05-26 14:54:43 +05:30
else :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( ~ Q ( cp = 0 ) , geom__within = pl , category = cat , event_name__isnull = True )
2022-08-26 18:31:54 +05:30
else :
2023-05-26 14:54:43 +05:30
if grp :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( geom__within = pl , category = cat , event_name__isnull = True , group__contains = grp , location_name__contains = ' 観光 ' )
2023-05-26 14:54:43 +05:30
else :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( geom__within = pl , category = cat , event_name__isnull = True , location_name__contains = ' 観光 ' )
2022-05-17 19:21:59 +05:30
else :
2023-05-26 14:54:43 +05:30
if is_rog :
if grp :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( ~ Q ( cp = 0 ) , geom__within = pl , event_name__isnull = True , group__contains = grp )
2023-05-26 14:54:43 +05:30
else :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( ~ Q ( cp = 0 ) , geom__within = pl , event_name__isnull = True )
2023-05-26 14:54:43 +05:30
else :
if grp :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( geom__within = pl , event_name__isnull = True , group__contains = grp , location_name__contains = ' 観光 ' )
2023-05-26 14:54:43 +05:30
else :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( geom__within = pl , event_name__isnull = True , location_name__contains = ' 観光 ' )
2024-07-31 00:56:23 +00:00
'''
if len ( locs ) > 200 :
2023-05-26 14:54:43 +05:30
return JsonResponse ( { " too_many_points " : True } , safe = False , status = 500 )
else :
serializer = LocationSerializer ( locs , many = True )
return JsonResponse ( serializer . data , safe = False )
else :
return JsonResponse ( { } , safe = False )
2022-05-16 18:36:38 +05:30
2022-03-30 16:17:48 +05:30
def SubInPerf ( request ) :
prefecture = request . GET . get ( ' perf ' )
perf_geom = JpnAdminMainPerf . objects . get ( id = prefecture )
sub = JpnAdminPerf . objects . filter ( geom__within = perf_geom . geom )
serializer = JPN_perfSerializer ( sub , many = True )
return JsonResponse ( serializer . data , safe = False )
2022-04-02 16:20:48 +05:30
2022-04-05 16:36:21 +05:30
def SubPerfInMainPerf ( request ) :
2022-06-06 21:15:07 +05:30
area = request . GET . get ( ' area ' )
perf_geom = GifuAreas . objects . get ( id = area )
sub = JpnSubPerf . objects . filter ( geom__contained = perf_geom . geom )
2022-04-27 15:47:37 +05:30
#serializer = JPN_sub_perSerializer
2022-04-05 16:36:21 +05:30
#sub = JpnAdminPerf.objects.filter(geom__within=perf_geom.geom)
serializer = JPN_sub_perSerializer ( sub , many = True )
return JsonResponse ( serializer . data , safe = False )
2022-06-06 21:15:07 +05:30
def GetAllGifuAreas ( request ) :
prefecture = request . GET . get ( ' perf ' )
perf_geom = JpnAdminMainPerf . objects . get ( id = prefecture )
sub = GifuAreas . objects . filter ( geom__contained = perf_geom . geom )
serializer = GifuAreaSerializer ( sub , many = True )
return JsonResponse ( serializer . data , safe = False )
2022-04-05 16:36:21 +05:30
def ExtentForMainPerf ( request ) :
perf_id = request . GET . get ( ' perf ' )
perf = JpnAdminMainPerf . objects . get ( id = perf_id )
ext = perf . geom . extent
# iata = serializers.serialize("json",ext)
return JsonResponse ( ext , safe = False )
2022-12-29 19:19:43 +05:30
@api_view ( [ ' POST ' , ] )
@permission_classes ( ( IsAuthenticated , ) )
@csrf_exempt
2022-12-27 18:01:18 +05:30
def ExtentForLocations ( request ) :
2022-12-29 19:19:43 +05:30
user = request . user
2023-01-20 17:18:07 +05:30
ec = user . event_code
2024-08-09 23:49:36 +00:00
print ( user . event_code )
logger . debug ( f " ExtentForLocations called for user: { user . email } , event_code: { ec } " )
if not ec :
logger . warning ( f " ExtentForLocations: No event_code for user { user . email } " )
return JsonResponse ( { " error " : " No event code associated with the user " } , status = status . HTTP_400_BAD_REQUEST )
try :
2025-08-31 17:35:06 +09:00
# event_codeに基づいてイベントを検索し、そのイベントに関連するLocation2025を取得
from rog . models import NewEvent2
from django . db . models import Q
# event_codeでイベントを検索( event_nameに含まれる場合も考慮)
events = NewEvent2 . objects . filter (
Q ( event_code = ec ) |
Q ( event_name__icontains = ec )
)
if not events . exists ( ) :
logger . warning ( f " No events found for event_code: { ec } " )
return JsonResponse ( { " error " : " No events found for the given event code " } , status = status . HTTP_404_NOT_FOUND )
# 見つかったイベントに関連するLocation2025を取得
locs = Location2025 . objects . filter ( event__in = events ) . aggregate ( Extent ( ' location ' ) , Union ( ' location ' ) )
2024-08-09 23:49:36 +00:00
2025-08-31 17:35:06 +09:00
if not locs [ ' location__extent ' ] :
2024-08-09 23:49:36 +00:00
logger . info ( f " No locations found for event_code: { ec } " )
return JsonResponse ( { " error " : " No locations found for the given event code " } , status = status . HTTP_404_NOT_FOUND )
logger . info ( f " Successfully retrieved extent for event_code: { ec } " )
2025-08-31 17:35:06 +09:00
return JsonResponse ( locs [ ' location__extent ' ] , safe = False )
2024-08-09 23:49:36 +00:00
except Exception as e :
logger . error ( f " Error in ExtentForLocations: { str ( e ) } " , exc_info = True )
return JsonResponse ( { " error " : " An error occurred while processing your request " } , status = status . HTTP_500_INTERNAL_SERVER_ERROR )
2025-08-30 02:20:25 +09:00
#locs = Location2025.objects.filter(group__contains=ec).aggregate(Extent('geom'), Union('geom'))
2024-08-09 23:49:36 +00:00
#return JsonResponse(locs['geom__extent'], safe=False)
2022-12-27 18:01:18 +05:30
2024-08-02 07:01:32 +00:00
def ExtentForSubPerf_old ( request ) :
2022-04-02 16:20:48 +05:30
sub_perf_id = request . GET . get ( ' sub_perf ' )
2022-04-05 16:36:21 +05:30
sub_perf = JpnSubPerf . objects . get ( id = sub_perf_id )
2022-04-02 16:20:48 +05:30
ext = sub_perf . geom . extent
# iata = serializers.serialize("json",ext)
return JsonResponse ( ext , safe = False )
2024-08-02 07:01:32 +00:00
def ExtentForSubPerf ( request ) :
sub_perf_id = request . GET . get ( ' sub_perf ' )
if not sub_perf_id :
return JsonResponse ( { " error " : " sub_perf parameter is required " } , status = 400 )
2022-04-27 15:47:37 +05:30
2024-08-02 07:01:32 +00:00
try :
sub_perf = JpnSubPerf . objects . get ( id = sub_perf_id )
ext = sub_perf . geom . extent
return JsonResponse ( ext , safe = False )
2025-08-31 14:10:16 +09:00
except JpnSubPerf . DoesNotExist :
2024-08-02 07:01:32 +00:00
return JsonResponse ( { " error " : " Specified sub_perf does not exist " } , status = 404 )
except Exception as e :
2025-08-31 14:10:16 +09:00
# テーブルが存在しない場合やその他のデータベースエラーをキャッチ
return JsonResponse ( { " error " : f " Service temporarily unavailable: { str ( e ) } " } , status = 503 )
2024-08-02 07:01:32 +00:00
2022-04-27 15:47:37 +05:30
def CatView ( request ) :
2023-05-26 14:54:43 +05:30
lat1 = float ( request . GET . get ( ' la1 ' ) )
lon1 = float ( request . GET . get ( ' ln1 ' ) )
lat2 = float ( request . GET . get ( ' la2 ' ) )
lon2 = float ( request . GET . get ( ' ln2 ' ) )
lat3 = float ( request . GET . get ( ' la3 ' ) )
lon3 = float ( request . GET . get ( ' ln3 ' ) )
lat4 = float ( request . GET . get ( ' la4 ' ) )
lon4 = float ( request . GET . get ( ' ln4 ' ) )
if ( lat1 != None and lon1 != None and lat2 != None and lon2 != None and lat3 != None and lon3 != None and lat4 != None and lon4 != None ) :
pl = geos . Polygon ( ( ( lon1 , lat1 ) , ( lon2 , lat2 ) , ( lon3 , lat3 ) , ( lon4 , lat4 ) , ( lon1 , lat1 ) ) , srid = 4326 )
2025-08-30 02:20:25 +09:00
#locs = Location2025.objects.filter(geom__within=pl)
c = Location2025 . objects . filter ( geom__within = pl ) . values ( ' category ' ) . distinct ( )
2023-05-26 14:54:43 +05:30
serializer = LocationCatSerializer ( c , many = True )
return JsonResponse ( serializer . data , safe = False )
else :
return null
2025-08-30 02:20:25 +09:00
c = Location2025 . objects . filter ( ) . values ( ' category ' ) . distinct ( )
2022-04-27 15:47:37 +05:30
serializer = LocationCatSerializer ( c , many = True )
return JsonResponse ( serializer . data , safe = False )
2022-07-24 14:17:45 +05:30
def CatByCity ( request ) :
city = request . GET . get ( ' city ' )
2023-05-26 14:54:43 +05:30
if ( city != None ) :
cilt_polygon = JpnSubPerf . objects . filter ( adm1_ja = city )
2025-08-30 02:20:25 +09:00
cats = Location2025 . objects . filter ( geom__within = cilt_polygon [ 0 ] . geom ) . values ( ' category ' ) . distinct ( )
2023-05-26 14:54:43 +05:30
serializer = LocationCatSerializer ( cats , many = True )
return JsonResponse ( serializer . data , safe = False )
else :
return None
2022-07-24 14:17:45 +05:30
2022-04-27 15:47:37 +05:30
class RegistrationAPI ( generics . GenericAPIView ) :
2024-07-26 12:34:54 +00:00
#serializer_class = CreateUserSerializer
serializer_class = UserRegistrationSerializer
2022-04-27 15:47:37 +05:30
def post ( self , request , * args , * * kwargs ) :
serializer = self . get_serializer ( data = request . data )
serializer . is_valid ( raise_exception = True )
user = serializer . save ( )
return Response ( {
" user " : UserSerializer ( user , context = self . get_serializer_context ( ) ) . data ,
" token " : AuthToken . objects . create ( user ) [ 1 ]
} )
2024-01-26 11:43:29 +05:30
2024-08-02 07:01:32 +00:00
class LoginView ( APIView ) :
def post ( self , request ) :
email = request . data . get ( ' email ' )
password = request . data . get ( ' password ' )
# デバッグコード
user = CustomUser . objects . filter ( email = email ) . first ( )
if user :
stored_hash = user . password
print ( f " Stored hashed password: { stored_hash } " )
is_valid = check_password ( raw_password , stored_hash )
print ( f " Password is valid during login: { is_valid } " )
user = authenticate ( request , username = email , password = raw_password )
if user :
token , _ = Token . objects . get_or_create ( user = user )
return Response ( { ' token ' : token . key } , status = status . HTTP_200_OK )
else :
return Response ( { ' error ' : ' Invalid credentials ' } , status = status . HTTP_400_BAD_REQUEST )
#user = authenticate(request, username=email, password=password)
#if user:
# token, _ = Token.objects.get_or_create(user=user)
# return Response({'token': token.key}, status=status.HTTP_200_OK)
#else:
# return Response({'error': 'Invalid credentials'}, status=status.HTTP_400_BAD_REQUEST)
class PasswordResetView ( APIView ) :
def post ( self , request ) :
email = request . data . get ( ' email ' )
user = CustomUser . objects . filter ( email = email ) . first ( )
if user :
token = default_token_generator . make_token ( user )
uid = urlsafe_base64_encode ( force_bytes ( user . pk ) )
reset_link = f " https://yourwebsite.com/reset-password/ { uid } / { token } / "
send_mail (
' Password Reset ' ,
f ' Click here to reset your password: { reset_link } ' ,
' noreply@yourwebsite.com ' ,
[ email ] ,
fail_silently = False ,
)
return Response ( { ' message ' : ' Password reset email sent ' } , status = status . HTTP_200_OK )
else :
return Response ( { ' error ' : ' User not found ' } , status = status . HTTP_400_BAD_REQUEST )
2022-04-27 15:47:37 +05:30
class LoginAPI ( generics . GenericAPIView ) :
2024-08-02 07:01:32 +00:00
2022-04-27 15:47:37 +05:30
serializer_class = LoginUserSerializer
def post ( self , request , * args , * * kwargs ) :
2025-09-06 03:31:51 +09:00
# より詳細なログ情報を収集
request_data = request . data
identifier = request_data . get ( ' identifier ' , ' not provided ' )
email = request_data . get ( ' email ' , ' not provided ' )
has_password = bool ( request_data . get ( ' password ' ) )
logger . info ( f " Login attempt - identifier: { identifier } , email: { email } , has_password: { has_password } " )
logger . debug ( f " Full request data keys: { list ( request_data . keys ( ) ) } " )
2025-09-05 23:20:10 +09:00
logger . debug ( f " Request headers: { dict ( request . headers ) } " )
2024-11-12 07:19:18 +09:00
2025-09-06 03:31:51 +09:00
# フロントエンドのフィールドマッピングを拡張
data = request_data . copy ( )
# identifierが提供された場合はemailにマッピング
2025-08-24 19:44:36 +09:00
if ' identifier ' in data and ' email ' not in data :
data [ ' email ' ] = data [ ' identifier ' ]
2025-09-06 03:31:51 +09:00
logger . info ( f " Mapped identifier ' { data [ ' identifier ' ] } ' to email field " )
# emailが直接提供されている場合はそのまま使用
elif ' email ' in data :
logger . info ( f " Email directly provided: { data [ ' email ' ] } " )
# どちらも提供されていない場合
else :
logger . error ( " Neither ' identifier ' nor ' email ' provided in request " )
return Response ( {
" error " : " メールアドレスが提供されていません。 " ,
" details " : " email or identifier field is required "
} , status = status . HTTP_400_BAD_REQUEST )
2025-09-05 23:20:10 +09:00
2025-09-06 03:31:51 +09:00
logger . debug ( f " Processed data for serializer: email= { data . get ( ' email ' ) } , has_password= { bool ( data . get ( ' password ' ) ) } " )
2025-08-24 19:44:36 +09:00
serializer = self . get_serializer ( data = data )
2024-11-12 07:19:18 +09:00
try :
serializer . is_valid ( raise_exception = True )
user = serializer . validated_data
logger . info ( f " User { user . email } logged in successfully " )
# ユーザー情報をシリアライズ
user_data = UserSerializer ( user , context = self . get_serializer_context ( ) ) . data
# 認証トークンを生成
token = AuthToken . objects . create ( user ) [ 1 ]
return Response ( {
" user " : user_data ,
" token " : token
} )
except serializers . ValidationError as e :
2025-09-06 03:31:51 +09:00
# より詳細なエラーログ
attempted_email = data . get ( ' email ' , ' unknown ' )
logger . error ( f " Login validation failed for email ' { attempted_email } ' : { e . detail } " )
2024-11-12 07:19:18 +09:00
logger . error ( f " Serializer errors: { serializer . errors } " )
2025-09-06 03:31:51 +09:00
# ユーザーフレンドリーなエラーメッセージ
if ' non_field_errors ' in serializer . errors :
error_detail = serializer . errors [ ' non_field_errors ' ] [ 0 ]
if ' Incorrect password ' in str ( error_detail ) :
error_msg = " パスワードが正しくありません。 "
elif ' User with this email does not exist ' in str ( error_detail ) :
error_msg = " このメールアドレスのユーザーは存在しません。 "
elif ' User account is disabled ' in str ( error_detail ) :
error_msg = " このアカウントは無効になっています。 "
else :
error_msg = " ログインに失敗しました。メールアドレスとパスワードを確認してください。 "
else :
error_msg = " ログインに失敗しました。入力内容を確認してください。 "
2024-11-12 07:19:18 +09:00
return Response ( {
" error " : error_msg ,
2025-09-06 03:31:51 +09:00
" field_errors " : serializer . errors
2024-11-12 07:19:18 +09:00
} , status = status . HTTP_400_BAD_REQUEST )
except Exception as e :
2025-09-06 03:31:51 +09:00
attempted_email = data . get ( ' email ' , ' unknown ' )
logger . error ( f " Unexpected error during login for email ' { attempted_email } ' : { str ( e ) } " )
2024-11-12 07:19:18 +09:00
logger . error ( f " Traceback: { traceback . format_exc ( ) } " )
2025-09-06 03:31:51 +09:00
return Response ( {
" error " : " ログイン処理中に予期しないエラーが発生しました。しばらく後に再試行してください。 "
} , status = status . HTTP_500_INTERNAL_SERVER_ERROR )
2024-11-12 07:19:18 +09:00
return Response ( {
" error " : " 予期せぬエラーが発生しました。 " ,
" details " : str ( e )
} , status = status . HTTP_500_INTERNAL_SERVER_ERROR )
def post_old ( self , request , * args , * * kwargs ) :
2024-08-02 07:01:32 +00:00
logger . info ( f " Login attempt for user: { request . data . get ( ' email ' , ' email not provided ' ) } " )
logger . debug ( f " Request data: { request . data } " )
2022-04-27 15:47:37 +05:30
serializer = self . get_serializer ( data = request . data )
2024-08-02 07:01:32 +00:00
try :
serializer . is_valid ( raise_exception = True )
user = serializer . validated_data
logger . info ( f " User { user . email } logged in successfully " )
return Response ( {
" user " : UserSerializer ( user , context = self . get_serializer_context ( ) ) . data ,
" token " : AuthToken . objects . create ( user ) [ 1 ]
} )
except serializers . ValidationError as e :
logger . error ( f " Login failed for user { request . data . get ( ' email ' , ' email not provided ' ) } : { str ( e ) } " )
logger . error ( f " Serializer errors: { serializer . errors } " )
error_msg = serializer . errors . get ( ' non_field_errors ' , [ ' ログインに失敗しました。 ' ] ) [ 0 ]
return Response ( {
" error " : error_msg ,
" details " : serializer . errors
} , status = status . HTTP_400_BAD_REQUEST )
except Exception as e :
logger . error ( f " Unexpected error during login for user { request . data . get ( ' email ' , ' email not provided ' ) } : { str ( e ) } " )
logger . error ( f " Traceback: { traceback . format_exc ( ) } " )
return Response ( {
" error " : " 予期せぬエラーが発生しました。 " ,
" details " : str ( e )
} , status = status . HTTP_500_INTERNAL_SERVER_ERROR )
2022-04-27 15:47:37 +05:30
2024-07-25 00:57:48 +00:00
class UserUpdateAPI ( generics . UpdateAPIView ) :
permission_classes = [ permissions . IsAuthenticated ]
serializer_class = UserUpdateSerializer
def get_object ( self ) :
return self . request . user
def update ( self , request , * args , * * kwargs ) :
partial = kwargs . pop ( ' partial ' , False )
instance = self . get_object ( )
serializer = self . get_serializer ( instance , data = request . data , partial = partial )
serializer . is_valid ( raise_exception = True )
self . perform_update ( serializer )
if getattr ( instance , ' _prefetched_objects_cache ' , None ) :
instance . _prefetched_objects_cache = { }
return Response ( serializer . data )
2024-07-26 14:54:24 +00:00
# User 情報取得
2022-04-27 15:47:37 +05:30
class UserAPI ( generics . RetrieveAPIView ) :
2022-10-18 14:58:20 +05:30
permission_classes = [ permissions . IsAuthenticated , ]
2022-04-27 15:47:37 +05:30
serializer_class = UserSerializer
def get_object ( self ) :
2022-05-12 02:15:36 +05:30
return self . request . user
2024-08-09 23:49:36 +00:00
@api_view ( [ ' POST ' , ] )
@permission_classes ( ( IsAuthenticated , ) )
@csrf_exempt
class GoalImageViewSet_old ( APIView ) :
permissions_classes = [ permissions . IsAuthenticated , ]
# parser_classes = [MultiPartParser, JSONParser]
2022-11-05 14:31:55 +05:30
2024-08-09 23:49:36 +00:00
def post ( self , request , format = None ) :
# print(request.data)
serializer = GolaImageSerializer ( data = request . data )
if serializer . is_valid ( ) :
serializer . save ( )
return Response ( serializer . data , status = status . HttP_200_OK )
else :
return Response ( serializer . errors , status = status . HTTP_400_BAD_REQUEST )
2022-11-05 14:31:55 +05:30
2024-08-09 23:49:36 +00:00
return Response ( { " ok " : " ok " } )
2022-10-18 14:58:20 +05:30
class GoalImageViewSet ( viewsets . ModelViewSet ) :
queryset = GoalImages . objects . all ( )
serializer_class = GolaImageSerializer
# parser_classes = (MultiPartParser, JSONParser)
def get_queryset ( self ) :
queryset = GoalImages . objects . all ( )
# dist = self.request.GET.get('dist')
# if dist != None :
# queryset = Incident.objects.filter(entity=dist, is_approved=True)
# else:
# queryset = Incident.objects.filter(is_approved=True)
return queryset
2022-11-05 18:11:00 +05:30
class CheckinImageViewSet ( viewsets . ModelViewSet ) :
queryset = CheckinImages . objects . all ( )
serializer_class = CheckinImageSerializer
# parser_classes = (MultiPartParser, JSONParser)
def get_queryset ( self ) :
2025-09-06 00:31:53 +09:00
logger . info ( f " CheckinImageViewSet: get_queryset called by user { self . request . user } " )
2022-11-05 18:11:00 +05:30
queryset = CheckinImages . objects . all ( )
2025-09-06 00:31:53 +09:00
logger . info ( f " CheckinImageViewSet: returning { queryset . count ( ) } checkin images " )
2022-11-05 18:11:00 +05:30
# dist = self.request.GET.get('dist')
# if dist != None :
# queryset = Incident.objects.filter(entity=dist, is_approved=True)
# else:
# queryset = Incident.objects.filter(is_approved=True)
return queryset
2024-01-26 11:43:29 +05:30
2025-09-06 00:31:53 +09:00
def create ( self , request , * args , * * kwargs ) :
logger . info ( f " CheckinImageViewSet: create called by user { request . user } " )
logger . info ( f " CheckinImageViewSet: request data = { request . data } " )
response = super ( ) . create ( request , * args , * * kwargs )
if response . status_code == 201 :
logger . info ( f " CheckinImageViewSet: checkin image created successfully, ID = { response . data . get ( ' id ' ) } " )
else :
logger . error ( f " CheckinImageViewSet: failed to create checkin image, status = { response . status_code } " )
return response
def update ( self , request , * args , * * kwargs ) :
logger . info ( f " CheckinImageViewSet: update called by user { request . user } for ID { kwargs . get ( ' pk ' ) } " )
return super ( ) . update ( request , * args , * * kwargs )
def destroy ( self , request , * args , * * kwargs ) :
logger . info ( f " CheckinImageViewSet: delete called by user { request . user } for ID { kwargs . get ( ' pk ' ) } " )
return super ( ) . destroy ( request , * args , * * kwargs )
2024-01-26 11:43:29 +05:30
class RetrieveUserView ( generics . RetrieveAPIView ) :
queryset = CustomUser . objects . all ( )
serializer_class = UserSerializer
permission_classes = [ IsAuthenticated ]
def get_object ( self ) :
return self . request . user
2022-11-05 18:11:00 +05:30
2022-05-12 02:15:36 +05:30
2022-06-08 21:40:43 +05:30
def userDetials ( request ) :
user_id = request . GET . get ( ' user_id ' )
user = CustomUser . objects . get ( id = user_id )
rogUser = RogUser . objects . filter ( user = user )
serializer = RogUserSerializer ( rogUser , many = True )
return JsonResponse ( serializer . data , safe = False )
2023-01-06 16:09:41 +05:30
@api_view ( [ ' GET ' ] )
@permission_classes ( ( IsAuthenticated , ) )
def DeleteAccount ( request ) :
usr = request . user ;
#print("user is" + usr)
if ( usr ) :
#usr.delete()
usr . email = usr . email + " _res " + str ( uuid . uuid4 ( ) )
usr . save ( ) ;
AuthToken . objects . filter ( user = usr ) . delete ( )
return Response ( { " result " : " user deleted " } )
return Response ( { " result " : " user not found " } )
2022-06-08 21:40:43 +05:30
2024-07-26 12:34:54 +00:00
'''
def send_verification_email ( self , temp_user ) :
subject = ' 仮登録の確認 '
message = f ' 以下のリンクをクリックして登録を完了してください: \n { settings . FRONTEND_URL } /verify/ { temp_user . verification_code } '
from_email = settings . DEFAULT_FROM_EMAIL
recipient_list = [ temp_user . email ]
try :
send_mail ( subject , message , from_email , recipient_list )
logger . info ( f " Verification email sent to { temp_user . email } " )
except Exception as e :
logger . error ( f " Failed to send verification email to { temp_user . email } . Error: { str ( e ) } " )
'''
2022-06-08 21:40:43 +05:30
2022-05-12 02:15:36 +05:30
def UserActionViewset ( request ) :
user_id = request . GET . get ( ' user_id ' )
location_id = request . GET . get ( ' location_id ' )
2025-08-30 02:20:25 +09:00
location = Location2025 . objects . get ( location_id = location_id )
2022-05-12 02:15:36 +05:30
user = CustomUser . objects . get ( id = user_id )
action = Useractions . objects . filter ( location = location , user = user )
serializer = UseractionsSerializer ( action , many = True )
return JsonResponse ( serializer . data , safe = False )
def UserMakeActionViewset ( request ) :
user_id = request . GET . get ( ' user_id ' )
location_id = request . GET . get ( ' location_id ' )
wanttogo = True if request . GET . get ( ' wanttogo ' ) == " true " else False
like = True if request . GET . get ( ' like ' ) == " true " else False
checkin = True if request . GET . get ( ' checkin ' ) == " true " else False
2025-09-06 00:31:53 +09:00
logger . info ( f " UserMakeActionViewset: user_id= { user_id } , location_id= { location_id } " )
logger . info ( f " UserMakeActionViewset: wanttogo= { wanttogo } , like= { like } , checkin= { checkin } " )
2025-08-30 02:20:25 +09:00
location = Location2025 . objects . get ( location_id = location_id )
2022-05-12 02:15:36 +05:30
user = CustomUser . objects . get ( id = user_id )
2025-09-06 00:31:53 +09:00
logger . info ( f " UserMakeActionViewset: user= { user . email } , location= { location . location_name } " )
2022-05-12 02:15:36 +05:30
#action = Useractions.objects.filter(location__id=location_id, user__id=user_id)
rec = Useractions . objects . filter ( user = user , location = location )
if ( rec ) :
2025-09-06 00:31:53 +09:00
logger . info ( f " UserMakeActionViewset: updating existing action record " )
2022-05-12 02:15:36 +05:30
obj = rec . update ( wanttogo = wanttogo , like = like , checkin = checkin )
2025-09-06 00:31:53 +09:00
if checkin :
logger . info ( f " UserMakeActionViewset: CHECKIN UPDATED for user { user . email } at location { location . location_name } " )
2022-05-12 02:15:36 +05:30
else :
2025-09-06 00:31:53 +09:00
logger . info ( f " UserMakeActionViewset: creating new action record " )
2022-05-12 02:15:36 +05:30
obj , created = Useractions . objects . update_or_create ( user = user , location = location , wanttogo = wanttogo , like = like , checkin = checkin )
2025-09-06 00:31:53 +09:00
if checkin and created :
logger . info ( f " UserMakeActionViewset: NEW CHECKIN CREATED for user { user . email } at location { location . location_name } " )
2022-05-12 02:15:36 +05:30
serializer = UseractionsSerializer ( obj , many = False )
return JsonResponse ( serializer . data , safe = False )
def UserDestinations ( request ) :
user_id = request . GET . get ( ' user_id ' )
user = CustomUser . objects . get ( id = user_id )
#action = Useractions.objects.filter(location__id=location_id, user__id=user_id)
2022-06-08 21:40:43 +05:30
rec = Useractions . objects . filter ( user = user , wanttogo = True ) . order_by ( ' order ' )
2022-05-12 02:15:36 +05:30
serializer = UserDestinationSerializer ( rec , many = True )
return JsonResponse ( serializer . data , safe = False )
def UpdateOrder ( request ) :
dir = request . GET . get ( ' dir ' )
user_action_id = int ( request . GET . get ( ' user_action_id ' ) )
order = int ( request . GET . get ( ' order ' ) )
aorder = int ( request . GET . get ( ' order ' ) )
oorder = int ( request . GET . get ( ' order ' ) )
if ( user_action_id ) :
#updated = Useractions.objects.filter(order__gte=order).update(order = F('order')+1)
#res = Useractions.objects.filter(id=user_action_id).update(order=order)
index = 0
if dir == " up " :
for id in Useractions . objects . all ( ) . order_by ( ' order ' ) . values_list ( ' id ' , flat = True ) :
print ( id )
print ( " ---- " , user_action_id )
if index == order :
index + = 1
print ( " index increated ..... " )
if user_action_id == id :
Useractions . objects . filter ( id = id ) . update ( order = order )
print ( " updated ..... " )
continue
Useractions . objects . filter ( id = id ) . update ( order = index )
index + = 1
else :
for id in Useractions . objects . all ( ) . order_by ( ' order ' ) . values_list ( ' id ' , flat = True ) :
print ( id )
print ( " ---- " , user_action_id )
if index == order :
index - = 1
print ( " index increated ..... " )
if user_action_id == id :
Useractions . objects . filter ( id = id ) . update ( order = order )
print ( " updated ..... " )
continue
Useractions . objects . filter ( id = id ) . update ( order = index )
index + = 1
# for id in Useractions.objects.values_list('order', flat=True):
# aorder -= 1
# Useractions.objects.filter(order__lt=id).update(order=aorder)
# res = Useractions.objects.filter(id=user_action_id).update(order=oorder)
return JsonResponse ( 1 , safe = False )
else :
return JsonResponse ( { } , safe = False )
2022-05-25 18:25:42 +05:30
def DeleteDestination ( request ) :
dest_id = int ( request . GET . get ( ' dest_id ' ) )
print ( " ###### dest " )
print ( dest_id )
if ( dest_id != None ) :
Useractions . objects . filter ( id = dest_id ) . delete ( )
return JsonResponse ( { " success " : 1 } , safe = False )
else :
return JsonResponse ( { " success " : 0 } , safe = False )
2022-06-06 21:15:07 +05:30
2023-05-26 14:54:43 +05:30
def CustomAreaLocations ( request ) :
cat = request . GET . get ( ' cat ' )
name = request . GET . get ( ' name ' )
is_rog = request . GET . get ( ' rog ' )
grp = request . GET . get ( ' grp ' )
2023-05-26 13:56:26 +05:30
2023-05-26 14:54:43 +05:30
if ( cat != None ) :
2022-08-23 19:08:56 +05:30
if is_rog :
2023-05-26 14:54:43 +05:30
if grp :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( ~ Q ( cp = 0 ) , event_name__isnull = False , category = cat , event_name = name , group__contains = grp )
2023-05-26 14:54:43 +05:30
else :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( ~ Q ( cp = 0 ) , event_name__isnull = False , category = cat , event_name = name )
2023-05-26 14:54:43 +05:30
else :
if grp :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( event_name__isnull = False , category = cat , event_name = name , group__contains = grp , location_name__contains = ' 観光 ' )
2023-05-26 14:54:43 +05:30
else :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( event_name__isnull = False , category = cat , event_name = name , location_name__contains = ' 観光 ' )
2023-05-26 14:54:43 +05:30
else :
if is_rog :
if grp :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( ~ Q ( cp = 0 ) , event_name__isnull = False , event_name = name , group__contains = grp )
2023-05-26 14:54:43 +05:30
else :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( ~ Q ( cp = 0 ) , event_name__isnull = False , event_name = name )
2023-05-26 14:54:43 +05:30
else :
if grp :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( event_name__isnull = False , event_name = name , group__contains = grp , location_name__contains = ' 観光 ' )
2023-05-26 14:54:43 +05:30
else :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( event_name__isnull = False , event_name = name , location_name__contains = ' 観光 ' )
2023-05-26 14:54:43 +05:30
serializer = LocationSerializer ( locs , many = True )
return JsonResponse ( serializer . data , safe = False )
2022-06-06 21:15:07 +05:30
2022-05-12 02:15:36 +05:30
2022-06-08 21:40:43 +05:30
def CustomAreaNames ( request ) :
2025-08-30 02:20:25 +09:00
locs = Location2025 . objects . filter ( event_name__isnull = False ) . values ( ' event_name ' ) . distinct ( )
2022-06-08 21:40:43 +05:30
serializer = LocationEventNameSerializer ( locs , many = True )
return JsonResponse ( serializer . data , safe = False )
2022-05-12 02:15:36 +05:30
2024-07-25 00:57:48 +00:00
class UserActivationView ( APIView ) :
2024-07-26 04:03:15 +00:00
def get ( self , request , activation_token ) :
try :
temp_user = TempUser . objects . get ( verification_code = activation_token )
user = CustomUser . objects . create (
email = temp_user . email ,
firstname = temp_user . firstname ,
lastname = temp_user . lastname ,
date_of_birth = temp_user . date_of_birth ,
female = temp_user . female ,
is_active = True
)
# Here you might want to add the user to the team they were invited to
temp_user . delete ( )
return Response ( { " message " : " アカウントが正常にアクティベートされました。 " } , status = status . HTTP_200_OK )
except TempUser . DoesNotExist :
return Response ( { " error " : " 無効なアクティベーショントークンです。 " } , status = status . HTTP_400_BAD_REQUEST )
'''
2024-07-25 00:57:48 +00:00
def get ( self , request , activation_token ) :
try :
user = CustomUser . objects . get ( activation_token = activation_token , is_active = False )
user . is_active = True
user . activation_token = None
user . save ( )
return Response ( { " message " : " アカウントが正常にアクティベートされました。 " } , status = status . HTTP_200_OK )
except CustomUser . DoesNotExist :
return Response ( { " error " : " 無効なアクティベーショントークンです。 " } , status = status . HTTP_400_BAD_REQUEST )
2024-07-26 04:03:15 +00:00
'''
2024-07-25 00:57:48 +00:00
2022-10-06 17:42:12 +05:30
class ChangePasswordView ( generics . UpdateAPIView ) :
"""
An endpoint for changing password .
"""
serializer_class = ChangePasswordSerializer
model = CustomUser
permission_classes = ( IsAuthenticated , )
def get_object ( self , queryset = None ) :
obj = self . request . user
return obj
def update ( self , request , * args , * * kwargs ) :
self . object = self . get_object ( )
serializer = self . get_serializer ( data = request . data )
if serializer . is_valid ( ) :
# Check old password
if not self . object . check_password ( serializer . data . get ( " old_password " ) ) :
return Response ( { " old_password " : [ " Wrong password. " ] } , status = status . HTTP_400_BAD_REQUEST )
# set_password also hashes the password that the user will get
self . object . set_password ( serializer . data . get ( " new_password " ) )
self . object . save ( )
response = {
' status ' : ' success ' ,
' code ' : status . HTTP_200_OK ,
' message ' : ' Password updated successfully ' ,
' data ' : [ ]
}
return Response ( response )
return Response ( serializer . errors , status = status . HTTP_400_BAD_REQUEST )
2022-05-12 02:15:36 +05:30
class TestActionViewSet ( viewsets . ModelViewSet ) :
serializer_class = TestSerialiser
2022-09-27 17:52:14 +05:30
queryset = TestModel . objects . all ( )
2023-01-09 17:11:03 +05:30
def PrivacyView ( request ) :
return render ( request , " rog/privacy.html " )
2024-07-26 12:34:54 +00:00
2023-05-03 16:12:53 +05:30
class RegistrationView ( APIView ) :
2024-07-26 12:34:54 +00:00
@transaction.atomic
2023-05-03 16:12:53 +05:30
def post ( self , request ) :
2024-07-26 12:34:54 +00:00
serializer = UserRegistrationSerializer ( data = request . data )
2023-05-03 16:12:53 +05:30
if serializer . is_valid ( ) :
2024-07-26 12:34:54 +00:00
try :
user = serializer . save ( )
logger . info ( f " New user registered: { user . email } " )
2024-08-02 07:01:32 +00:00
# パスワードを取得
password = serializer . validated_data . pop ( ' password ' )
# ユーザーを作成するが、まだ保存しない
user = serializer . save ( commit = False )
# パスワードを明示的に設定
user . set_password ( password )
# ユーザーを保存
user . save ( )
logger . info ( f " New user registered: { user . email } " )
2024-07-26 12:34:54 +00:00
return Response ( { " message " : " ユーザー登録が完了しました。 " } , status = status . HTTP_201_CREATED )
2024-08-02 07:01:32 +00:00
2024-07-26 12:34:54 +00:00
except Exception as e :
logger . error ( f " Error during user registration: { str ( e ) } " )
return Response ( { " error " : " ユーザー登録中にエラーが発生しました。 " } , status = status . HTTP_500_INTERNAL_SERVER_ERROR )
else :
logger . warning ( f " Invalid registration data: { serializer . errors } " )
return Response ( serializer . errors , status = status . HTTP_400_BAD_REQUEST )
2024-07-24 00:38:32 +00:00
# Akira
2024-07-25 00:57:48 +00:00
class NewEvent2ViewSet ( viewsets . ModelViewSet ) :
queryset = NewEvent2 . objects . all ( )
serializer_class = NewEvent2Serializer
2024-11-12 08:59:20 +09:00
def get_permissions ( self ) :
"""
GETメソッドは認証不要 、 その他のメソッドは認証必要
"""
if self . action in [ ' list ' , ' retrieve ' ] :
2024-11-12 09:09:00 +09:00
permission_classes = [ permissions . AllowAny ]
2024-11-12 08:59:20 +09:00
else :
2024-11-12 09:09:00 +09:00
permission_classes = [ permissions . IsAuthenticated ]
2024-11-12 08:59:20 +09:00
return [ permission ( ) for permission in permission_classes ]
2024-07-25 00:57:48 +00:00
class NewEvent2ListView ( generics . ListAPIView ) :
queryset = NewEvent2 . objects . all ( )
serializer_class = NewEvent2Serializer
2024-09-03 13:19:30 +00:00
permission_classes = [ permissions . IsAuthenticated ]
2024-07-25 00:57:48 +00:00
2024-07-24 00:38:32 +00:00
class NewEventViewSet ( viewsets . ModelViewSet ) :
queryset = NewEvent . objects . all ( )
serializer_class = NewEventSerializer
2024-11-12 09:09:00 +09:00
permission_classes = [ permissions . IsAuthenticated ]
2024-07-24 00:38:32 +00:00
class NewEventListView ( generics . ListAPIView ) :
queryset = NewEvent . objects . all ( )
serializer_class = NewEventSerializer
2024-11-12 09:09:00 +09:00
permission_classes = [ permissions . IsAuthenticated ]
2024-07-24 00:38:32 +00:00
2024-08-01 07:51:52 +00:00
2024-07-31 00:56:23 +00:00
2024-07-24 00:38:32 +00:00
class TeamViewSet ( viewsets . ModelViewSet ) :
serializer_class = TeamSerializer
2024-07-25 00:57:48 +00:00
permission_classes = [ permissions . IsAuthenticated , IsTeamOwner ]
def get_queryset ( self ) :
return Team . objects . filter ( owner = self . request . user )
2024-07-24 00:38:32 +00:00
def perform_create ( self , serializer ) :
2024-08-01 07:51:52 +00:00
logger . info ( f " Creating new team for user: { self . request . user . email } " )
2024-08-04 18:56:11 +00:00
team = serializer . save ( owner = self . request . user )
logger . info ( f " Team created successfully: { team . id } " )
2024-08-01 07:51:52 +00:00
2024-08-04 18:56:11 +00:00
def update_related_entries ( self , team ) :
current_entries = Entry . objects . filter ( team = team , is_active = True )
for entry in current_entries :
self . update_external_system ( entry . zekken_number , team . owner . event_code , team . team_name )
2024-08-01 07:51:52 +00:00
2024-08-04 18:56:11 +00:00
def update_external_system ( self , zekken_number , event_code , team_name ) :
2024-08-01 07:51:52 +00:00
api_url = f " { settings . FRONTEND_URL } /gifuroge/update_team_name "
2024-08-04 18:56:11 +00:00
headers = { " Content-Type " : " application/x-www-form-urlencoded " }
2024-08-01 07:51:52 +00:00
data = {
2024-10-21 12:48:42 +00:00
#"zekken_number": zekken_number,
2024-08-01 07:51:52 +00:00
" new_team_name " : team_name ,
2024-10-21 12:48:42 +00:00
#"event_code": event_code,
2024-08-01 07:51:52 +00:00
}
try :
2024-08-04 18:56:11 +00:00
response = requests . post ( api_url , headers = headers , data = data )
2024-08-01 07:51:52 +00:00
response . raise_for_status ( )
2024-08-04 18:56:11 +00:00
logger . info ( f " External system updated for zekken_number: { zekken_number } " )
2024-08-01 07:51:52 +00:00
return True
except requests . RequestException as e :
logger . error ( f " Failed to update external system. Error: { str ( e ) } " )
return False
2024-08-04 18:56:11 +00:00
# with transaction.atomic():
#
# category = serializer.validated_data['category']
# category = NewCategory.objects.select_for_update().get(id=category.id)
# zekken_number = category.category_number
# category.category_number = F('category_number') + 1
# category.save()
# category.refresh_from_db() # F() 式の結果を評価
#
# team = serializer.save(owner=self.request.user, zekken_number=zekken_number)
# logger.info(f"Team created successfully: {team.id}")
#
# # 外部システムの更新
# success = self.register_team(
# team.owner,
# team.zekken_number,
# team.owner.event_code,
# team.team_name,
# team.category.category_name,
# team.owner.password
# )
# if not success:
# logger.error("Failed to register external system")
# raise serializers.ValidationError("外部システムの更新に失敗しました。")
# logger.info("External system register successfully")
#
# def register_team(self, owner,zekken_number,event_code,team_name,category_name,password):
# logger.info(f"register_team ==> zekken_number={zekken_number},event_code={event_code},team_name={team_name},category_name={category_name},password={password}")
# api_url = f"{settings.FRONTEND_URL}/gifuroge/register_team"
# user = owner
#
# headers = {
# "Content-Type": "application/x-www-form-urlencoded"
# }
# data = {
# "zekken_number": zekken_number,
# "event_code": event_code,
# "team_name": team_name,
# "class_name": category_name,
# "password": password # パスワードの扱いに注意が必要です
# }
#
# try:
# response = requests.post(api_url,headers=headers,data=data)
# response.raise_for_status()
# logger.info(f"Team registered successfully for team {team_name}")
# return True
#
# except requests.RequestException as e:
# logger.error(f"Failed to register team for entry {entry.id}. Error: {str(e)}")
# # エラーが発生しても、エントリー自体は作成されています
# # 必要に応じて、ここでエラーハンドリングを追加できます
# return False
#
# def destroy(self, request, *args, **kwargs):
# team = self.get_object()
# if team.members.exists():
# return Response(
# {
# "error": "チームにメンバーが残っているため削除できません。",
# "error_code": "TEAM_HAS_MEMBERS"
# },
# status=status.HTTP_400_BAD_REQUEST
# )
#
# #return Response({"error": "チームにメンバーが残っているため削除できません。"}, status=status.HTTP_400_BAD_REQUEST)
# return super().destroy(request, *args, **kwargs)
#
# def update_external_system(zekken_number, event_code, team_name, class_name, password):
# api_url = f"{settings.FRONTEND_URL}/gifuroge/update_team_name"
# headers = {
# "Content-Type": "application/x-www-form-urlencoded"
# }
# data = {
# "zekken_number": zekken_number,
# "new_team_name": team_name,
# "event_code": event_code,
# }
# try:
# response = requests.post(api_url,headers=headers, data=data)
# response.raise_for_status()
#
# return True
# except requests.RequestException as e:
# logger.error(f"Failed to update external system. Error: {str(e)}")
# return False
#
2024-08-01 07:51:52 +00:00
# def update(self, request, *args, **kwargs):
# try:
2024-08-04 18:56:11 +00:00
# partial = kwargs.pop('partial', False)
# instance = self.get_object()
# serializer = self.get_serializer(instance, data=request.data, partial=partial)
# serializer.is_valid(raise_exception=True)
# self.perform_update(serializer)
#
# if getattr(instance, '_prefetched_objects_cache', None):
# instance._prefetched_objects_cache = {}
#
# return Response(serializer.data)
2024-08-01 07:51:52 +00:00
# except Exception as e:
# return Response({"error": "更新に失敗しました。競合が発生した可能性があります。"}, status=status.HTTP_409_CONFLICT)
2024-08-04 18:56:11 +00:00
#
#
## def update(self, request, *args, **kwargs):
## try:
## return super().update(request, *args, **kwargs)
## except Exception as e:
## return Response({"error": "更新に失敗しました。競合が発生した可能性があります。"}, status=status.HTTP_409_CONFLICT)
#
# def perform_update(self, serializer):
# with transaction.atomic():
# team = serializer.save()
#
# # 外部システムの更新
# success = update_external_system(
# team.zekken_number,
# team.owner.event_code,
# team.team_name,
# team.category.category_name,
# team.owner.password
# )
# if not success:
# raise serializers.ValidationError("外部システムの更新に失敗しました。")
# else:
# print("岐阜ロゲシステム更新に成功")
#
# @action(detail=True, methods=['post'])
# def copy(self, request, pk=None):
# original_team = self.get_object()
# with transaction.atomic():
# category = NewCategory.objects.select_for_update().get(id=original_team.category.id)
# new_zekken_number = category.category_number
# category.category_number = F('category_number') + 1
# category.save()
# category.refresh_from_db() # F() 式の結果を評価
#
# new_team = Team.objects.create(
# zekken_number=new_zekken_number,
# team_name=f"{original_team.team_name} (コピー)",
# category=category,
# owner=request.user
# )
# for member in original_team.members.all():
# Member.objects.create(
# team=new_team,
# user=member.user
# )
# return Response(TeamSerializer(new_team).data, status=status.HTTP_201_CREATED)
#
2024-07-25 00:57:48 +00:00
2025-08-20 19:15:19 +09:00
class CategoryDetailView ( generics . RetrieveAPIView ) :
serializer_class = NewCategorySerializer
def get_object ( self ) :
name = unquote ( self . kwargs [ ' name ' ] ) # URLデコード
return get_object_or_404 ( NewCategory , category_name = name )
2025-01-24 17:49:41 +09:00
class CategoryByNameView ( generics . RetrieveAPIView ) :
serializer_class = NewCategorySerializer
def get_object ( self ) :
category_name = self . kwargs . get ( ' name ' )
return get_object_or_404 ( NewCategory , category_name = category_name )
2024-07-25 00:57:48 +00:00
class NewCategoryViewSet ( viewsets . ModelViewSet ) :
queryset = NewCategory . objects . all ( )
serializer_class = NewCategorySerializer
2024-08-04 18:56:11 +00:00
#permission_classes = [IsAuthenticated]
2024-11-12 08:59:20 +09:00
def get_permissions ( self ) :
"""
GETメソッドは認証不要 、 その他のメソッドは認証必要
"""
if self . action in [ ' list ' , ' retrieve ' ] :
2024-11-12 09:09:00 +09:00
permission_classes = [ permissions . AllowAny ]
2024-11-12 08:59:20 +09:00
else :
2024-11-12 09:09:00 +09:00
permission_classes = [ permissions . IsAuthenticated ]
2024-11-12 08:59:20 +09:00
return [ permission ( ) for permission in permission_classes ]
2024-08-04 18:56:11 +00:00
@action ( detail = True , methods = [ ' POST ' ] )
def get_zekken_number ( self , request , pk = None ) :
try :
with transaction . atomic ( ) :
category = NewCategory . objects . select_for_update ( ) . get ( pk = pk )
category . category_number + = 1
category . save ( )
serializer = self . get_serializer ( category )
return Response ( serializer . data )
except NewCategory . DoesNotExist :
return Response ( { " error " : " Category not found " } , status = status . HTTP_404_NOT_FOUND )
except Exception as e :
return Response ( { " error " : str ( e ) } , status = status . HTTP_400_BAD_REQUEST )
2024-07-25 00:57:48 +00:00
class NewCategoryListView ( generics . ListAPIView ) :
queryset = NewCategory . objects . all ( )
serializer_class = NewCategorySerializer
2024-11-12 08:59:20 +09:00
#permission_classes = [IsAuthenticated]
def get_permissions ( self ) :
"""
GETメソッドは認証不要 、 その他のメソッドは認証必要
"""
2025-01-22 08:19:49 +00:00
if self . request . method == ' GET ' :
2024-11-12 09:09:00 +09:00
permission_classes = [ permissions . AllowAny ]
2024-11-12 08:59:20 +09:00
else :
2024-11-12 09:09:00 +09:00
permission_classes = [ permissions . IsAuthenticated ]
2025-01-22 08:19:49 +00:00
return [ permission ( ) for permission in permission_classes ]
2024-11-12 08:59:20 +09:00
2024-07-24 00:38:32 +00:00
class CategoryViewSet ( viewsets . ModelViewSet ) :
queryset = Category . objects . all ( )
serializer_class = CategorySerializer
2024-11-12 09:09:00 +09:00
permission_classes = [ permissions . IsAuthenticated ]
2024-07-24 00:38:32 +00:00
class CategoryListView ( generics . ListAPIView ) :
queryset = Category . objects . all ( )
serializer_class = CategorySerializer
2024-11-12 09:09:00 +09:00
permission_classes = [ permissions . IsAuthenticated ]
2024-07-24 00:38:32 +00:00
'''
def get ( self , request ) :
categories = Category . objects . all ( )
data = [ ]
for category in categories :
category_name = force_str ( category . category_name )
data . append ( {
' category_name ' : category_name ,
# その他のフィールド
} )
return Response ( data )
'''
class EntryViewSet ( viewsets . ModelViewSet ) :
2024-07-26 04:03:15 +00:00
#queryset = Entry.objects.all()
2024-07-24 00:38:32 +00:00
serializer_class = EntrySerializer
permission_classes = [ permissions . IsAuthenticated ]
2024-07-25 00:57:48 +00:00
#def perform_create(self, serializer):
# team = Team.objects.get(owner=self.request.user)
# serializer.save(team=team)
def get_queryset ( self ) :
user = self . request . user
2024-07-26 12:34:54 +00:00
# ユーザーが所有するチームのIDを取得
owned_team_ids = Team . objects . filter ( owner = user ) . values_list ( ' id ' , flat = True )
# ユーザーがメンバーとして所属するチームのIDを取得
member_team_ids = Member . objects . filter ( user = user ) . values_list ( ' team_id ' , flat = True )
# 両方のチームに関連するエントリーを取得
2025-01-23 08:22:59 +09:00
return Entry . objects . filter ( Q ( team__id__in = owned_team_ids ) | Q ( team__id__in = member_team_ids ) , is_active = True )
2025-01-23 07:42:55 +09:00
#return Entry.objects.filter(Q(team__id__in=owned_team_ids) | Q(team__id__in=member_team_ids))
2024-07-26 12:34:54 +00:00
2024-08-04 18:56:11 +00:00
@transaction.atomic
2024-07-24 00:38:32 +00:00
def perform_create ( self , serializer ) :
2024-08-26 09:02:01 +00:00
try :
category = serializer . validated_data [ ' category ' ]
category = NewCategory . objects . select_for_update ( ) . get ( id = category . id )
zekken_number = category . category_number
category . category_number = F ( ' category_number ' ) + 1
category . save ( )
category . refresh_from_db ( )
team = serializer . validated_data [ ' team ' ]
event = serializer . validated_data [ ' event ' ]
event_name = event . event_name # イベント名を取得
entry = serializer . save ( owner = self . request . user , zekken_number = zekken_number )
logger . info ( f " team.owner = { team . owner } , event_name = { event_name } " )
logger . info ( f " team = { team } " )
2025-09-06 00:11:57 +09:00
# 外部システムの更新を試行(失敗してもエントリー作成は継続)
2024-08-26 09:02:01 +00:00
success = self . register_team (
entry . zekken_number ,
event_name ,
team . team_name ,
category . category_name ,
team . owner . password
)
if not success :
2025-09-06 00:11:57 +09:00
logger . warning ( " Failed to register external system, but entry was created successfully " )
# 外部システム登録失敗をGpsLogに記録( 後でリトライ可能)
from . models import GpsLog
GpsLog . record_external_registration_request ( entry )
logger . info ( " External system registration request recorded in GpsLog for later retry " )
else :
logger . info ( " External system registered successfully " )
2024-08-26 09:02:01 +00:00
except Exception as e :
2025-09-06 03:21:36 +09:00
logger . error ( f " Error creating Entry: { e } " )
if hasattr ( e , ' message_dict ' ) :
logger . error ( f " Validation details: { e . message_dict } " )
# perform_createで例外を発生させて、createメソッドでキャッチ
raise e
2024-08-26 09:02:01 +00:00
2024-08-04 18:56:11 +00:00
2024-08-09 23:49:36 +00:00
def create ( self , request , * args , * * kwargs ) :
serializer = self . get_serializer ( data = request . data )
serializer . is_valid ( raise_exception = True )
2024-09-03 13:19:30 +00:00
# イベントの締め切り日時をチェック
event = serializer . validated_data [ ' event ' ]
if event . deadlineDateTime and timezone . now ( ) > event . deadlineDateTime :
return Response (
{ " error " : " 締め切りを過ぎているため、エントリーを作成できません。 " } ,
status = status . HTTP_400_BAD_REQUEST
)
2024-08-09 23:49:36 +00:00
try :
self . perform_create ( serializer )
headers = self . get_success_headers ( serializer . data )
return Response ( serializer . data , status = status . HTTP_201_CREATED , headers = headers )
except DjangoValidationError as e :
2025-09-06 03:21:36 +09:00
# バリデーションエラーの詳細をログに記録
error_details = e . message_dict if hasattr ( e , ' message_dict ' ) else str ( e )
logger . error ( f " Entry validation error: { error_details } " )
# ユーザーフレンドリーなエラーメッセージを作成
if ' __all__ ' in error_details and ' 男性のみ参加可能 ' in str ( error_details ) :
error_message = " 選択されたカテゴリーは男性のみ参加可能です。適切なカテゴリーを選択してください。 "
elif ' __all__ ' in error_details and ' 女性のみ参加可能 ' in str ( error_details ) :
error_message = " 選択されたカテゴリーは女性のみ参加可能です。適切なカテゴリーを選択してください。 "
else :
error_message = str ( e )
return Response ( { " error " : error_message } , status = status . HTTP_400_BAD_REQUEST )
except Exception as e :
# その他のエラー
logger . exception ( f " Unexpected error creating Entry: { str ( e ) } " )
return Response (
{ " error " : " エントリー作成中に予期しないエラーが発生しました。しばらく後に再試行してください。 " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
2024-08-09 23:49:36 +00:00
2024-08-04 18:56:11 +00:00
def register_team ( self , zekken_number , event_code , team_name , category_name , password ) :
api_url = f " { settings . FRONTEND_URL } /gifuroge/register_team "
headers = { " Content-Type " : " application/x-www-form-urlencoded " }
data = {
" zekken_number " : zekken_number ,
" event_code " : event_code ,
" team_name " : team_name ,
" class_name " : category_name ,
" password " : password
}
2024-08-09 23:49:36 +00:00
logger . info ( f " data= { data } " ) ;
2024-07-31 00:56:23 +00:00
try :
2024-08-04 18:56:11 +00:00
response = requests . post ( api_url , headers = headers , data = data )
response . raise_for_status ( )
logger . info ( f " Team registered successfully for zekken_number { zekken_number } " )
return True
except requests . RequestException as e :
logger . error ( f " Failed to register team. Error: { str ( e ) } " )
return False
2024-07-31 00:56:23 +00:00
2024-08-09 23:49:36 +00:00
def update ( self , request , * args , * * kwargs ) :
logger . info ( f " Update method called for Entry with ID: { kwargs . get ( ' pk ' ) } " )
logger . debug ( f " Request data: { request . data } " )
partial = kwargs . pop ( ' partial ' , False )
instance = self . get_object ( )
serializer = self . get_serializer ( instance , data = request . data , partial = partial )
try :
serializer . is_valid ( raise_exception = True )
logger . debug ( f " Serializer validated data: { serializer . validated_data } " )
except serializers . ValidationError as e :
logger . error ( f " Validation error: { e . detail } " )
return Response ( { " error " : str ( e ) } , status = status . HTTP_400_BAD_REQUEST )
try :
self . perform_update ( serializer )
logger . info ( f " Entry updated successfully: { serializer . data } " )
except Exception as e :
logger . exception ( f " Error updating Entry: { str ( e ) } " )
return Response ( { " error " : " An error occurred while updating the entry. " } , status = status . HTTP_500_INTERNAL_SERVER_ERROR )
#if getattr(instance, '_prefetched_objects_cache', None):
# instance._prefetched_objects_cache = {}
return Response ( serializer . data )
2024-07-31 00:56:23 +00:00
2024-08-04 18:56:11 +00:00
# def create(self, request, *args, **kwargs):
# serializer = self.get_serializer(data=request.data)
# try:
# serializer.is_valid(raise_exception=True)
# self.perform_create(serializer)
# headers = self.get_success_headers(serializer.data)
# return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
# except DRFValidationError as e:
# return Response({'error': self.format_errors(e.detail)}, status=status.HTTP_400_BAD_REQUEST)
# # except IntegrityError:
# # return Response({'error': '既に登録済みです'}, status=status.HTTP_400_BAD_REQUEST)
# except IntegrityError as e:
# return Response({'error': f'データベースエラー: {str(e)}'}, status=status.HTTP_400_BAD_REQUEST)
#
# except Exception as e:
# return Response({'error': f"予期せぬエラーが発生しました: {str(e),'type': str(type(e))}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
#
# def perform_create(self, serializer):
# serializer.save(owner=self.request.user)
#
#
# def update(self, request, *args, **kwargs):
# logger.info(f"Update method called for Entry with ID: {kwargs.get('pk')}")
# logger.debug(f"Request data: {request.data}")
#
# partial = kwargs.pop('partial', False)
# instance = self.get_object()
# serializer = self.get_serializer(instance, data=request.data, partial=partial)
#
# try:
# serializer.is_valid(raise_exception=True)
# logger.debug(f"Serializer validated data: {serializer.validated_data}")
# except serializers.ValidationError as e:
# logger.error(f"Validation error: {e.detail}")
# return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
#
# try:
# self.perform_update(serializer)
# logger.info(f"Entry updated successfully: {serializer.data}")
# except Exception as e:
# logger.exception(f"Error updating Entry: {str(e)}")
# return Response({"error": "An error occurred while updating the entry."}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
#
#
# return Response(serializer.data)
#
# def destroy(self, request, *args, **kwargs):
# instance = self.get_object()
# self.perform_destroy(instance)
# return Response(status=status.HTTP_204_NO_CONTENT)
#
# def get_error_message(self, exception):
# if hasattr(exception, 'detail'):
# if isinstance(exception.detail, dict):
# return '. '.join([f"{key}: {', '.join(value)}" for key, value in exception.detail.items()])
# elif isinstance(exception.detail, list):
# return '. '.join(exception.detail)
# return str(exception)
#
# def format_errors(self, errors):
# if isinstance(errors, list):
# return '. '.join(errors)
# elif isinstance(errors, dict):
# return '. '.join([f"{key}: {value}" if isinstance(value, str) else f"{key}: {', '.join(value)}" for key, value in errors.items()])
# else:
# return str(errors)
2024-07-26 04:03:15 +00:00
2024-08-26 09:02:01 +00:00
class TeamEntriesView ( generics . ListAPIView ) :
serializer_class = EntrySerializer
permission_classes = [ IsAuthenticated , IsTeamOwnerOrMember ]
def get_queryset ( self ) :
team_id = self . kwargs [ ' team_id ' ]
team = Team . objects . get ( id = team_id )
return Entry . objects . filter ( team = team )
def get_serializer_context ( self ) :
context = super ( ) . get_serializer_context ( )
context [ ' team_id ' ] = self . kwargs [ ' team_id ' ]
return context
2024-07-26 04:03:15 +00:00
2024-07-24 00:38:32 +00:00
class MemberViewSet ( viewsets . ModelViewSet ) :
2024-08-01 07:51:52 +00:00
serializer_class = MemberSerializer
2024-07-25 00:57:48 +00:00
permission_classes = [ permissions . IsAuthenticated , IsTeamOwner ]
2024-07-26 04:03:15 +00:00
def get_serializer_class ( self ) :
if self . action == ' create ' :
return MemberCreationSerializer
return MemberSerializer
2024-07-25 00:57:48 +00:00
def get_queryset ( self ) :
team_id = self . kwargs [ ' team_id ' ]
return Member . objects . filter ( team_id = team_id )
2024-08-01 07:51:52 +00:00
@action ( detail = False , methods = [ ' DELETE ' ] )
def destroy_all ( self , request , team_id = None ) :
team = Team . objects . get ( id = team_id )
# チームのオーナーかどうかを確認
if team . owner != request . user :
return Response ( { " error " : " チームのオーナーのみがメンバーを一括削除できます。 " } , status = status . HTTP_403_FORBIDDEN )
# 確認パラメータをチェック
confirm = request . query_params . get ( ' confirm ' , ' ' ) . lower ( )
if confirm != ' true ' :
return Response ( { " error " : " 確認パラメータが必要です。 ' ?confirm=true ' を追加してください。 " } , status = status . HTTP_400_BAD_REQUEST )
try :
with transaction . atomic ( ) :
# オーナー以外のすべてのメンバーを削除
#deleted_count = Member.objects.filter(team=team).exclude(user=team.owner).delete()[0]
deleted_count = Member . objects . filter ( team = team ) . delete ( ) [ 0 ]
return Response ( {
" message " : f " { deleted_count } 人のメンバーが削除されました。 " ,
" deleted_count " : deleted_count
} , status = status . HTTP_200_OK )
except Exception as e :
return Response ( { " error " : str ( e ) } , status = status . HTTP_500_INTERNAL_SERVER_ERROR )
def destroy ( self , request , * args , * * kwargs ) :
instance = self . get_object ( )
team = instance . team
# チームのオーナーかどうかを確認
if team . owner != request . user :
return Response ( { " error " : " チームのオーナーのみがメンバーを削除できます。 " } , status = status . HTTP_403_FORBIDDEN )
# オーナー自身は削除できないようにする
#if instance.user == team.owner:
# return Response({"error": "チームのオーナーは削除できません。"}, status=status.HTTP_400_BAD_REQUEST)
self . perform_destroy ( instance )
return Response ( status = status . HTTP_204_NO_CONTENT )
2024-07-26 04:03:15 +00:00
def create ( self , request , * args , * * kwargs ) :
2024-08-01 07:51:52 +00:00
logger . info ( f " Attempting to create new member for team: { self . kwargs [ ' team_id ' ] } " )
logger . debug ( f " Received data: { request . data } " )
2024-07-26 04:03:15 +00:00
team = Team . objects . get ( id = self . kwargs [ ' team_id ' ] )
2024-08-01 07:51:52 +00:00
#serializer = self.get_serializer(data=request.data)
serializer = self . get_serializer ( data = request . data , context = { ' team ' : team } )
try :
serializer . is_valid ( raise_exception = True )
except serializers . ValidationError as e :
logger . error ( f " Validation error: { e . detail } " )
return Response ( { " error " : str ( e ) } , status = status . HTTP_400_BAD_REQUEST )
2024-07-26 04:03:15 +00:00
email = serializer . validated_data . get ( ' email ' , ' ' )
2024-08-01 07:51:52 +00:00
logger . info ( f " Processing member with email: { email } " )
2024-07-26 04:03:15 +00:00
user_data = {
' firstname ' : serializer . validated_data . get ( ' firstname ' , ' ' ) ,
' lastname ' : serializer . validated_data . get ( ' lastname ' , ' ' ) ,
' date_of_birth ' : serializer . validated_data . get ( ' date_of_birth ' ) ,
' female ' : serializer . validated_data . get ( ' female ' , False ) ,
}
2024-08-01 07:51:52 +00:00
# 自分自身を登録する場合
if request . user . email == email :
member , created = Member . objects . get_or_create ( user = request . user , team = team )
if created :
return Response ( MemberSerializer ( member ) . data , status = status . HTTP_201_CREATED )
else :
return Response ( { " message " : " You are already a member of this team. " } , status = status . HTTP_200_OK )
2024-07-26 04:03:15 +00:00
if not email or email . startswith ( ' dummy_ ' ) :
2024-08-01 07:51:52 +00:00
logger . info ( " Processing as direct registration " )
2024-07-26 04:03:15 +00:00
# 直接登録
2024-08-01 07:51:52 +00:00
try :
if email :
user , created = CustomUser . objects . get_or_create (
email = email ,
defaults = { * * user_data , ' is_active ' : True }
)
else :
# emailが空の場合、一意のダミーメールアドレスを生成
dummy_email = f " dummy_ { uuid . uuid4 ( ) } @example.com "
user = CustomUser . objects . create ( email = dummy_email , * * user_data )
2024-07-26 04:03:15 +00:00
2024-08-01 07:51:52 +00:00
member = Member . objects . create ( user = user , team = team )
logger . info ( f " Member created successfully: { member . id } " )
return Response ( MemberSerializer ( member ) . data , status = status . HTTP_201_CREATED )
except Exception as e :
logger . error ( f " Error creating member: { str ( e ) } " )
return Response ( { " error " : str ( e ) } , status = status . HTTP_400_BAD_REQUEST )
2024-07-26 04:03:15 +00:00
#member = serializer.save(team=team)
#return Response(serializer.data, status=status.HTTP_201_CREATED)
else :
2024-08-01 07:51:52 +00:00
logger . info ( " Processing as temporary registration " )
2024-08-09 23:49:36 +00:00
try :
# 仮登録
existing_user = CustomUser . objects . filter ( email = email ) . first ( )
if existing_user :
logger . info ( f " Existing user found: { existing_user . id } " )
# 既存ユーザーの場合、チーム招待メールを送信
send_team_join_email ( request , request . user , existing_user , team )
return Response ( { " message " : " Invitation for your team sent to existing user. " } , status = status . HTTP_200_OK )
else :
logger . info ( " Inviting new temporary user " )
try :
print ( " 新規ユーザー " )
#temp_user = TempUser.objects.create(
# email=email,
# **user_data,
# verification_code=str(uuid.uuid4())
#)
send_invitation_email ( request . user , request , email , team ) #仮登録済みでも確認メールを送る。
logger . info ( f " Invitation email sent to: { email } " )
return Response ( { " message " : " Invitation email sent to the user. " } , status = status . HTTP_201_CREATED )
except Exception as e :
logger . info ( f " Error on Invitation email sent to: { email } : { e } " )
return Response ( { " error " : str ( e ) } , status = status . HTTP_400_BAD_REQUEST )
except Exception as e :
logger . error ( f " Error in create method: { str ( e ) } " )
return Response ( { " error " : " An unexpected error occurred. " } , status = status . HTTP_500_INTERNAL_SERVER_ERROR )
2024-07-26 04:03:15 +00:00
'''
2024-07-25 00:57:48 +00:00
def create ( self , request , * args , * * kwargs ) :
team = Team . objects . get ( id = self . kwargs [ ' team_id ' ] )
serializer = self . get_serializer ( data = request . data )
serializer . is_valid ( raise_exception = True )
try :
self . perform_create ( serializer )
except DRFValidationError as e :
return Response ( { " error " : str ( e ) } , status = status . HTTP_400_BAD_REQUEST )
#except ValidationError as e:
# return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
except IntegrityError :
return Response ( { " error " : " このユーザーは既にチームのメンバーです。 " } , status = status . HTTP_400_BAD_REQUEST )
headers = self . get_success_headers ( serializer . data )
return Response ( serializer . data , status = status . HTTP_201_CREATED , headers = headers )
2024-07-26 04:03:15 +00:00
'''
2024-07-26 12:34:54 +00:00
'''
2024-07-26 04:03:15 +00:00
def send_activation_email ( self , temp_user , team ) :
# アクティベーションメール送信のロジックをここに実装
# 注意: 'user-activation' URLパターンが存在しない場合は、適切なURLを生成する方法を検討する必要があります
activation_url = " dummy url " #f"{settings.FRONTEND_URL}/activate/{temp_user.verification_code}"
# ここでemailを送信するロジックを実装
print ( f " Activation email would be sent to { temp_user . email } with URL: { activation_url } " )
def send_invitation_email ( self , user , team ) :
# チーム招待メール送信のロジック
#invitation_url = self.request.build_absolute_uri(
# reverse('team-invite', kwargs={'team_id': team.id, 'user_id': user.id})
#)
#subject = f"Invitation to join {team.name}"
#message = f"You have been invited to join the team {team.name}. Click here to accept: {invitation_url}"
#from_email = settings.DEFAULT_FROM_EMAIL
#recipient_list = [user.email]
#send_mail(subject, message, from_email, recipient_list)
# チーム招待メール送信のロジックをここに実装
# 注意: 'team-invitation' URLパターンが存在しない場合は、適切なURLを生成する方法を検討する必要があります
# 例えば、フロントエンドのURLを直接指定する方法など
invitation_url = " dummy url " #f"{settings.FRONTEND_URL}/team/{team.id}/invite"
# ここでemailを送信するロジックを実装
print ( f " Invitation email would be sent to { user . email } with URL: { invitation_url } " )
2024-07-26 12:34:54 +00:00
'''
2024-07-26 04:03:15 +00:00
2024-07-25 00:57:48 +00:00
def perform_create ( self , serializer ) :
team = Team . objects . get ( id = self . kwargs [ ' team_id ' ] )
serializer . save ( team = team )
2024-07-26 12:34:54 +00:00
@transaction.atomic
2024-07-25 00:57:48 +00:00
def update ( self , request , * args , * * kwargs ) :
2024-07-26 12:34:54 +00:00
#partial = kwargs.pop('partial', False)
2024-07-25 00:57:48 +00:00
instance = self . get_object ( )
2024-07-26 12:34:54 +00:00
user = instance . user
logger . debug ( f " Updating user: { user . email } " )
logger . debug ( f " Received data: { request . data } " )
if user . email . startswith ( ' dummy_ ' ) :
logger . debug ( " User has dummy email, proceeding with update " )
# 直接データを更新
user . firstname = request . data . get ( ' firstname ' , user . firstname )
user . lastname = request . data . get ( ' lastname ' , user . lastname )
# 日付の処理
date_of_birth = request . data . get ( ' date_of_birth ' )
if date_of_birth :
try :
date_of_birth = date_of_birth . translate ( str . maketrans ( " 0 1 2 3 4 5 6 7 8 9 " , " 0123456789 " ) )
parsed_date = parse_date ( date_of_birth )
if parsed_date :
user . date_of_birth = parsed_date
else :
raise ValueError ( " Invalid date format " )
except ValueError :
logger . error ( f " Invalid date format: { date_of_birth } " )
return Response ( { " error " : " Invalid date format. Use YYYY-MM-DD. " } , status = status . HTTP_400_BAD_REQUEST )
user . female = request . data . get ( ' female ' , user . female )
user . save ( )
logger . debug ( f " User updated: firstname= { user . firstname } , lastname= { user . lastname } , date_of_birth= { user . date_of_birth } , female= { user . female } " )
# Memberインスタンスも更新
serializer = self . get_serializer ( instance , data = request . data , partial = True )
serializer . is_valid ( raise_exception = True )
self . perform_update ( serializer )
# 更新後のデータを取得
updated_instance = self . get_object ( )
updated_serializer = self . get_serializer ( updated_instance )
logger . debug ( f " Updated user data: { updated_serializer . data } " )
return Response ( updated_serializer . data )
else :
logger . debug ( " User does not have dummy email, update not allowed " )
return Response ( { " error " : " このユーザーの情報は更新できません。 " } , status = status . HTTP_403_FORBIDDEN )
2024-07-25 00:57:48 +00:00
def perform_update ( self , serializer ) :
serializer . save ( )
2024-07-26 12:34:54 +00:00
logger . debug ( " perform_update called " )
2024-07-25 00:57:48 +00:00
def get_object ( self ) :
queryset = self . get_queryset ( )
member_id = self . kwargs [ ' pk ' ]
obj = get_object_or_404 ( queryset , id = member_id )
self . check_object_permissions ( self . request , obj )
return obj
2024-08-01 07:51:52 +00:00
class ActivateMemberView ( APIView ) :
def get ( self , request , user_id , team_id ) :
user = get_object_or_404 ( CustomUser , id = user_id )
team = get_object_or_404 ( Team , id = team_id )
# メンバーオブジェクトを取得または作成
member , created = Member . objects . get_or_create ( user = user , team = team )
if member . lastname == " dummy " :
# userデータでmemberデータを更新
member . lastname = user . lastname
member . firstname = user . firstname
member . date_of_birth = user . date_of_birth
member . female = user . female
member . save ( )
message = f " { team . team_name } に正常に参加し、メンバー情報が更新されました。アプリのチーム管理で確認できます。 "
else :
message = f " { team . team_name } に正常に参加しました。 "
return render ( request , ' activation_success.html ' , { ' message ' : message } )
class ActivateNewMemberView ( APIView ) :
def get ( self , request , verification_code , team_id ) :
try :
team = Team . objects . get ( id = team_id )
member , created = Member . objects . get_or_create ( team = team , user__email = email )
if created :
return Response ( { ' message ' : ' Member activated successfully ' } , status = status . HTTP_201_CREATED )
else :
return Response ( { ' message ' : ' Member already exists ' } , status = status . HTTP_200_OK )
except Team . DoesNotExist :
return Response ( { ' message ' : ' Invalid team ' } , status = status . HTTP_400_BAD_REQUEST )
'''
temp_user = get_object_or_404 ( TempUser , verification_code = verification_code )
team = get_object_or_404 ( Team , id = team_id )
user = CustomUser . objects . create_user (
email = temp_user . email ,
password = temp_user . password , # Ensure this is securely handled
* * { k : v for k , v in temp_user . __dict__ . items ( ) if k in [ ' firstname ' , ' lastname ' , ' date_of_birth ' , ' female ' ] }
)
Member . objects . create ( user = user , team = team )
temp_user . delete ( )
message = f " アカウントが作成され、 { team . team_name } に参加しました。アプリのチーム管理で確認できます。 "
return render ( request , ' activation_success.html ' , { ' message ' : message } )
'''
2024-07-25 00:57:48 +00:00
class OwnerEntriesView ( generics . ListAPIView ) :
serializer_class = EntrySerializer
permission_classes = [ permissions . IsAuthenticated ]
def get_queryset ( self ) :
user = self . request . user
return Entry . objects . filter ( owner = user )
class OwnerTeamsView ( generics . ListAPIView ) :
serializer_class = TeamSerializer
permission_classes = [ permissions . IsAuthenticated ]
def get_queryset ( self ) :
user = self . request . user
return Team . objects . filter ( owner = user )
class OwnerMembersView ( generics . ListAPIView ) :
2024-07-24 00:38:32 +00:00
serializer_class = MemberSerializer
permission_classes = [ permissions . IsAuthenticated ]
2024-07-25 00:57:48 +00:00
def get_queryset ( self ) :
user = self . request . user
return Member . objects . filter ( team__owner = user )
class MemberAddView ( APIView ) :
def post ( self , request , team_id ) :
logger . info ( f " Received request to add member to team { team_id } " )
logger . debug ( f " Request data: { request . data } " )
team = get_object_or_404 ( Team , id = team_id )
logger . info ( f " Found team: { team } " )
serializer = MemberSerializer ( data = request . data )
if serializer . is_valid ( ) :
logger . info ( " Serializer is valid " )
try :
member = serializer . save ( team = team )
logger . info ( f " Member added successfully: { member } " )
return Response ( serializer . data , status = status . HTTP_201_CREATED )
except Exception as e :
logger . error ( f " Error saving member: { str ( e ) } " )
return Response ( { " detail " : str ( e ) } , status = status . HTTP_400_BAD_REQUEST )
else :
logger . error ( f " Serializer errors: { serializer . errors } " )
return Response ( serializer . errors , status = status . HTTP_400_BAD_REQUEST )
2024-07-24 00:38:32 +00:00
class TempUserViewSet ( viewsets . ModelViewSet ) :
queryset = TempUser . objects . all ( )
serializer_class = TempUserSerializer
permission_classes = [ IsAuthenticated ]
# CustomUserViewSetの修正
class CustomUserViewSet ( viewsets . ModelViewSet ) :
queryset = CustomUser . objects . all ( )
serializer_class = CustomUserSerializer
permission_classes = [ IsAuthenticated ]
def get_queryset ( self ) :
user = self . request . user
if user . is_staff :
return CustomUser . objects . all ( )
return CustomUser . objects . filter ( id = user . id )
class TeamMembersView ( generics . ListAPIView ) :
serializer_class = MemberSerializer
permission_classes = [ IsAuthenticated ]
def get_queryset ( self ) :
team_id = self . kwargs [ ' team_id ' ]
return Member . objects . filter ( team_id = team_id )
# ユーザーのエントリーを取得するビュー
class UserEntriesView ( generics . ListAPIView ) :
serializer_class = EntrySerializer
permission_classes = [ IsAuthenticated ]
def get_queryset ( self ) :
user = self . request . user
return Entry . objects . filter ( team__owner = user )
# イベントのカテゴリーを取得するビュー
class EventCategoriesView ( generics . ListAPIView ) :
2024-07-26 04:03:15 +00:00
serializer_class = NewCategorySerializer
2024-07-24 00:38:32 +00:00
permission_classes = [ IsAuthenticated ]
def get_queryset ( self ) :
event_id = self . kwargs [ ' event_id ' ]
2024-07-26 04:03:15 +00:00
return NewCategory . objects . filter ( entry__event_id = event_id ) . distinct ( )
2024-07-24 00:38:32 +00:00
2024-07-26 12:34:54 +00:00
# ユーザー仮登録
2024-07-24 00:38:32 +00:00
class RegisterView ( APIView ) :
def post ( self , request ) :
serializer = TempUserSerializer ( data = request . data )
if serializer . is_valid ( ) :
temp_user = serializer . save ( )
verification_code = uuid . uuid4 ( )
temp_user . verification_code = verification_code
temp_user . save ( )
verification_url = request . build_absolute_uri (
2025-01-22 08:19:49 +00:00
reverse ( ' rog:verify-email ' , kwargs = { ' verification_code ' : verification_code } )
2024-07-24 00:38:32 +00:00
)
2025-09-03 21:56:40 +09:00
rog_utils_module . send_verification_email ( temp_user , verification_url )
2024-07-26 12:34:54 +00:00
#send_mail(
# 'Verify your email',
# f'Click the link to verify your email: {verification_url}',
# settings.DEFAULT_FROM_EMAIL,
# [temp_user.email],
# fail_silently=False,
#)
2024-07-24 00:38:32 +00:00
return Response ( { ' message ' : ' Verification email sent ' } , status = status . HTTP_201_CREATED )
return Response ( serializer . errors , status = status . HTTP_400_BAD_REQUEST )
2024-07-26 14:54:24 +00:00
class ResendInvitationEmailView ( APIView ) :
def post ( self , request ) :
email = request . data . get ( ' email ' )
if not email :
return Response ( { " error " : " メールアドレスを指定してください。 " } , status = status . HTTP_400_BAD_REQUEST )
try :
temp_user = TempUser . objects . get ( email = email )
verification_url = request . build_absolute_uri (
2025-01-22 08:19:49 +00:00
reverse ( ' rog:verify-email ' , kwargs = { ' verification_code ' : temp_user . verification_code } )
2024-07-26 14:54:24 +00:00
)
send_verification_email ( temp_user , verification_url )
logger . info ( f " 招待メールを再送信しました。Email: { email } " )
return Response ( { " message " : " 招待メールを再送信しました。 " } , status = status . HTTP_200_OK )
2025-08-31 14:10:16 +09:00
except TempUser . DoesNotExist :
2024-07-26 14:54:24 +00:00
logger . warning ( f " 仮登録されていないメールアドレスに対して招待メールの再送信が試みられました。Email: { email } " )
return Response ( { " error " : " 指定されたメールアドレスは仮登録されていません。 " } , status = status . HTTP_404_NOT_FOUND )
2024-07-26 12:34:54 +00:00
class TempUserRegistrationView ( APIView ) :
def post ( self , request ) :
2024-07-26 14:54:24 +00:00
email = request . data . get ( ' email ' )
2025-08-31 12:08:36 +09:00
# レート制限: 同じIPアドレスからの重複リクエストを防ぐ
client_ip = self . get_client_ip ( request )
cache_key_ip = f " register_rate_limit: { client_ip } "
cache_key_email = f " register_email_limit: { email } "
# 同じIPからの連続リクエストを制限( 1分間に最大3回)
ip_requests = cache . get ( cache_key_ip , 0 )
if ip_requests > = 3 :
logger . warning ( f " レート制限: IP { client_ip } からの過剰なリクエスト " )
return Response ( {
" error " : " リクエストが多すぎます。1分後に再試行してください。 "
} , status = status . HTTP_429_TOO_MANY_REQUESTS )
# 同じメールアドレスへの連続リクエストを制限( 30秒間に1回)
email_request_time = cache . get ( cache_key_email )
current_time = time . time ( )
if email_request_time and ( current_time - email_request_time ) < 30 :
logger . warning ( f " メール送信制限: { email } への重複リクエスト " )
return Response ( {
" error " : " 同じメールアドレスへの再送信は30秒後に可能です。 "
} , status = status . HTTP_429_TOO_MANY_REQUESTS )
2024-07-26 14:54:24 +00:00
# 本登録済みのユーザーチェック
if CustomUser . objects . filter ( email = email ) . exists ( ) :
logger . warning ( f " 既に本登録されているメールアドレスでの仮登録が試みられました。Email: { email } " )
return Response ( { " error " : " このメールアドレスは既に本登録されています。 " } , status = status . HTTP_400_BAD_REQUEST )
# 仮登録済みのユーザーチェック
try :
temp_user = TempUser . objects . get ( email = email )
2024-07-26 12:34:54 +00:00
verification_url = request . build_absolute_uri (
2025-01-22 08:19:49 +00:00
reverse ( ' rog:verify-email ' , kwargs = { ' verification_code ' : temp_user . verification_code } )
2024-07-26 12:34:54 +00:00
)
2024-07-26 14:54:24 +00:00
send_verification_email ( temp_user , verification_url )
2025-08-31 12:08:36 +09:00
# キャッシュを更新
cache . set ( cache_key_ip , ip_requests + 1 , 60 ) # 1分間
cache . set ( cache_key_email , current_time , 30 ) # 30秒間
2024-07-26 14:54:24 +00:00
logger . info ( f " 既に仮登録されているユーザーに招待メールを再送信しました。Email: { email } " )
return Response ( { " message " : " 既に仮登録は行われていますが、招待メールを再送信しました。 " } , status = status . HTTP_200_OK )
except TempUser . DoesNotExist :
# 新規仮登録
serializer = TempUserRegistrationSerializer ( data = request . data )
if serializer . is_valid ( ) :
2024-08-02 14:21:50 +00:00
# シリアライザのvalidated_dataからパスワードを取得
password = serializer . validated_data . get ( ' password ' )
# パスワードをハッシュ化
hashed_password = make_password ( password )
# ハッシュ化されたパスワードでTempUserを作成
temp_user = serializer . save ( password = hashed_password )
2024-07-26 14:54:24 +00:00
verification_code = uuid . uuid4 ( )
temp_user . verification_code = verification_code
temp_user . save ( )
2024-08-02 14:21:50 +00:00
2024-07-26 14:54:24 +00:00
verification_url = request . build_absolute_uri (
2025-01-22 08:19:49 +00:00
reverse ( ' rog:verify-email ' , kwargs = { ' verification_code ' : verification_code } )
2024-07-26 14:54:24 +00:00
)
send_verification_email ( temp_user , verification_url )
2025-08-31 12:08:36 +09:00
# キャッシュを更新
cache . set ( cache_key_ip , ip_requests + 1 , 60 ) # 1分間
cache . set ( cache_key_email , current_time , 30 ) # 30秒間
2024-07-26 14:54:24 +00:00
logger . info ( f " 新規ユーザーを仮登録し、招待メールを送信しました。Email: { email } " )
return Response ( { " message " : " 仮登録が完了しました。招待メールを送信しました。 " } , status = status . HTTP_201_CREATED )
return Response ( serializer . errors , status = status . HTTP_400_BAD_REQUEST )
2025-08-31 12:08:36 +09:00
def get_client_ip ( self , request ) :
""" クライアントIPアドレスを取得 """
x_forwarded_for = request . META . get ( ' HTTP_X_FORWARDED_FOR ' )
if x_forwarded_for :
ip = x_forwarded_for . split ( ' , ' ) [ 0 ]
else :
ip = request . META . get ( ' REMOTE_ADDR ' )
return ip
2024-07-26 14:54:24 +00:00
#serializer = TempUserRegistrationSerializer(data=request.data)
#if serializer.is_valid():
# temp_user = serializer.save()
# verification_code = uuid.uuid4()
# temp_user.verification_code = verification_code
# temp_user.save()
# verification_url = request.build_absolute_uri(
# reverse('verify-email', kwargs={'verification_code': verification_code})
# )
# send_verification_email(temp_user,verification_url) #招待メールを送る。
# return Response({"message": "仮登録が完了しました。招待メールを送信しました。"}, status=status.HTTP_201_CREATED)
#return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
2024-07-26 12:34:54 +00:00
# アクティベーション
2024-07-24 00:38:32 +00:00
class VerifyEmailView ( APIView ) :
def get ( self , request , verification_code ) :
try :
temp_user = TempUser . objects . get ( verification_code = verification_code )
if temp_user . is_valid ( ) :
user_data = {
' email ' : temp_user . email ,
2024-08-02 14:21:50 +00:00
' is_rogaining ' : True , # ここでis_rogainingをTrueに設定
' password ' : temp_user . password ,
2024-07-24 00:38:32 +00:00
' is_rogaining ' : temp_user . is_rogaining ,
' zekken_number ' : temp_user . zekken_number ,
' event_code ' : temp_user . event_code ,
' team_name ' : temp_user . team_name ,
' group ' : temp_user . group ,
' firstname ' : temp_user . firstname ,
' lastname ' : temp_user . lastname ,
' date_of_birth ' : temp_user . date_of_birth ,
' female ' : temp_user . female ,
}
2024-08-02 07:01:32 +00:00
# パスワードを安全にハッシュ化
#hashed_password = make_password(temp_user.password)
try :
# CustomUserを作成
2024-08-02 14:21:50 +00:00
user = CustomUser . objects . create ( * * user_data )
#user = CustomUser.objects.create_user(
# email=user_data['email'],
# password=temp_user.password, # ハッシュ化されたパスワードを直接使用
# **{k: v for k, v in user_data.items() if k != 'email'}
#)
2024-08-02 07:01:32 +00:00
except ValidationError as e :
# パスワードのバリデーションエラーなどの処理
return render ( request , ' verification_error.html ' , { ' message ' : str ( e ) , ' title ' : ' エラー ' } )
2024-07-24 00:38:32 +00:00
2024-08-01 07:51:52 +00:00
# チームへの追加処理(もし必要なら)
if hasattr ( temp_user , ' team_id ' ) :
team = Team . objects . get ( id = temp_user . team_id )
Member . objects . create ( user = user , team = team )
message = f " メールアドレスが確認され、アカウントが作成されました。 { team . team_name } のメンバーとして登録されています。アプリでログインして、必要な情報を入力してください。 "
else :
message = " メールアドレスが確認され、アカウントが作成されました。アプリでログインして、必要な情報を入力してください。 "
temp_user . delete ( )
2024-08-01 17:50:15 +00:00
return render ( request , ' verification_success.html ' , { ' message ' : message , ' title ' : " 認証成功 " } )
2024-07-24 00:38:32 +00:00
else :
2024-08-01 07:51:52 +00:00
message = " 確認リンクの有効期限が切れています。アプリから認証コードの再送信をしてください。 "
2024-08-01 17:50:15 +00:00
return render ( request , ' verification_success.html ' , { ' message ' : message , ' title ' : ' 有効期限切れ ' } )
2024-08-01 07:51:52 +00:00
2024-07-24 00:38:32 +00:00
except TempUser . DoesNotExist :
2024-08-01 07:51:52 +00:00
message = " 無効な確認コードです。 "
2024-08-01 17:50:15 +00:00
return render ( request , ' verification_success.html ' , { ' message ' : message , ' title ' : ' 無効な確認コードです ' } )
2024-07-24 00:38:32 +00:00
class MemberUserDetailView ( generics . RetrieveAPIView ) :
serializer_class = MemberWithUserSerializer
permission_classes = [ IsAuthenticated ]
def get_queryset ( self ) :
return Member . objects . select_related ( ' user ' , ' team ' )
class TeamMembersWithUserView ( generics . ListAPIView ) :
serializer_class = MemberWithUserSerializer
permission_classes = [ IsAuthenticated ]
def get_queryset ( self ) :
team_id = self . kwargs [ ' team_id ' ]
return Member . objects . filter ( team_id = team_id ) . select_related ( ' user ' , ' team ' )
2024-08-02 14:21:50 +00:00
class PasswordResetRequestView ( APIView ) :
def post ( self , request ) :
serializer = PasswordResetRequestSerializer ( data = request . data )
if serializer . is_valid ( ) :
email = serializer . validated_data [ ' email ' ]
user = CustomUser . objects . filter ( email = email ) . first ( )
if user :
token = default_token_generator . make_token ( user )
uid = urlsafe_base64_encode ( force_bytes ( user . pk ) )
reset_link = f " { settings . FRONTEND_URL } /api/reset-password/ { uid } / { token } / "
2025-09-03 21:56:40 +09:00
rog_utils_module . send_reset_password_email ( email , reset_link )
2024-08-02 14:21:50 +00:00
return Response ( { " message " : " Password reset email sent " } , status = status . HTTP_200_OK )
return Response ( { " message " : " User not found " } , status = status . HTTP_404_NOT_FOUND )
return Response ( serializer . errors , status = status . HTTP_400_BAD_REQUEST )
class PasswordResetConfirmView ( APIView ) :
def get ( self , request , uidb64 , token ) :
try :
uid = force_str ( urlsafe_base64_decode ( uidb64 ) )
user = CustomUser . objects . get ( pk = uid )
except ( TypeError , ValueError , OverflowError , CustomUser . DoesNotExist ) :
user = None
if user is not None and default_token_generator . check_token ( user , token ) :
return render ( request , ' password_reset.html ' , { ' uid ' : uidb64 , ' token ' : token } )
else :
return render ( request , ' password_reset_invalid.html ' )
if user is not None and default_token_generator . check_token ( user , token ) :
return Response ( { " message " : " Token is valid " } , status = status . HTTP_200_OK )
return Response ( { " message " : " Invalid reset link " } , status = status . HTTP_400_BAD_REQUEST )
def post ( self , request , uidb64 , token ) :
try :
uid = force_str ( urlsafe_base64_decode ( uidb64 ) )
user = CustomUser . objects . get ( pk = uid )
except ( TypeError , ValueError , OverflowError , CustomUser . DoesNotExist ) :
return Response ( { " error " : " Invalid reset link " } , status = status . HTTP_400_BAD_REQUEST )
if default_token_generator . check_token ( user , token ) :
serializer = PasswordResetConfirmSerializer ( data = request . data )
if serializer . is_valid ( ) :
user . set_password ( serializer . validated_data [ ' new_password ' ] )
user . save ( )
return Response ( { " message " : " Password has been reset successfully " } , status = status . HTTP_200_OK )
return Response ( serializer . errors , status = status . HTTP_400_BAD_REQUEST )
return Response ( { " error " : " Invalid reset link " } , status = status . HTTP_400_BAD_REQUEST )
2024-08-09 23:49:36 +00:00
class UserLastGoalTimeView ( APIView ) :
def get ( self , request , user_id ) :
try :
user = CustomUser . objects . get ( id = user_id )
last_goal = GoalImages . objects . filter (
user = user
) . aggregate ( last_goal_time = Max ( ' goaltime ' ) )
if last_goal [ ' last_goal_time ' ] :
return Response ( {
' user_email ' : user . email ,
' last_goal_time ' : last_goal [ ' last_goal_time ' ]
} )
else :
return Response ( {
' message ' : ' No goal records found for this user '
} , status = status . HTTP_404_NOT_FOUND )
except CustomUser . DoesNotExist :
return Response ( {
' message ' : ' User not found '
} , status = status . HTTP_404_NOT_FOUND )
2024-10-28 02:20:28 +00:00
# ----- for Supervisor -----
@api_view ( [ ' GET ' ] )
2024-10-28 20:25:05 +00:00
def debug_urls ( request ) :
""" デバッグ用: 利用可能なURLパターンを表示 """
resolver = get_resolver ( )
urls = [ ]
for pattern in resolver . url_patterns :
try :
urls . append ( str ( pattern . pattern ) )
except :
urls . append ( str ( pattern ) )
return JsonResponse ( { ' urls ' : urls } )
2024-10-28 02:20:28 +00:00
@api_view ( [ ' GET ' ] )
2024-10-28 20:25:05 +00:00
def get_events ( request ) :
logger . debug ( f " get_events was called. Path: { request . path } " )
try :
events = NewEvent2 . objects . filter ( public = True )
logger . debug ( f " Found { events . count ( ) } events " ) # デバッグ用ログ
return Response ( [ {
' code ' : event . event_name ,
' name ' : event . event_name ,
' start_datetime ' : event . start_datetime ,
' end_datetime ' : event . end_datetime ,
} for event in events ] )
logger . debug ( f " Returning data: { data } " ) # デバッグ用ログ
return JsonResponse ( data , safe = False )
except Exception as e :
print ( f " Error in get_events: { str ( e ) } " ) # デバッグ用
return Response (
{ " error " : " Failed to retrieve events " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
@api_view ( [ ' GET ' ] )
2024-10-28 02:20:28 +00:00
def get_zekken_numbers ( request , event_code ) :
2025-08-24 19:44:36 +09:00
# 通過審査画面用: GpsCheckinテーブルから過去の移行データと新規Entryテーブルの両方をサポート
try :
2025-09-06 00:31:53 +09:00
logger . info ( f " GET_ZEKKEN_NUMBERS: called with event_code: { event_code } by user { request . user . email if request . user . is_authenticated else ' Anonymous ' } " )
2025-08-24 19:44:36 +09:00
# event_codeからNewEvent2のIDを取得
try :
event_obj = NewEvent2 . objects . get ( event_code = event_code )
event_id = event_obj . id
2025-09-06 00:31:53 +09:00
logger . info ( f " GET_ZEKKEN_NUMBERS: Found event: { event_obj . event_name } (ID: { event_id } ) " )
2025-08-24 19:44:36 +09:00
except NewEvent2 . DoesNotExist :
2025-09-06 00:31:53 +09:00
logger . warning ( f " GET_ZEKKEN_NUMBERS: No event found with event_code: { event_code } , trying legacy data only " )
2025-08-24 19:44:36 +09:00
event_id = None
entry_list = [ ]
# 新規のEntryテーブルから取得( event_idが見つかった場合のみ)
if event_id :
entries = Entry . objects . filter (
event_id = event_id ,
zekken_number__gt = 0
2025-08-29 20:25:18 +09:00
) . values_list ( ' zekken_label ' , flat = True ) . order_by ( ' zekken_number ' )
2025-08-24 19:44:36 +09:00
if event_id :
entries = Entry . objects . filter (
event_id = event_id ,
zekken_number__gt = 0
2025-08-29 20:25:18 +09:00
) . values_list ( ' zekken_label ' , flat = True ) . order_by ( ' zekken_number ' )
2025-08-24 19:44:36 +09:00
entry_list = list ( entries )
2025-09-06 00:31:53 +09:00
logger . info ( f " GET_ZEKKEN_NUMBERS: Entry table found { len ( entry_list ) } records " )
2025-08-24 19:44:36 +09:00
# GpsCheckinテーブルからも検索( 過去の移行データ)
gps_checkins = GpsCheckin . objects . filter (
event_code = event_code ,
2025-08-29 16:38:01 +09:00
zekken__gt = ' '
) . values_list ( ' zekken ' , flat = True ) . distinct ( ) . order_by ( ' zekken ' )
2025-08-24 19:44:36 +09:00
gps_list = list ( gps_checkins )
2025-09-06 00:31:53 +09:00
logger . info ( f " GET_ZEKKEN_NUMBERS: GpsCheckin table found { len ( gps_list ) } records " )
2025-08-24 19:44:36 +09:00
# 両方の結果をマージして重複を除去
all_zekken_numbers = entry_list + gps_list
unique_zekken_numbers = sorted ( set ( all_zekken_numbers ) )
print ( f " Final result: { unique_zekken_numbers [ : 10 ] } " )
return Response ( unique_zekken_numbers )
except Exception as e :
print ( f " Error in get_zekken_numbers: { str ( e ) } " )
import traceback
traceback . print_exc ( )
return Response ( { " error " : str ( e ) } , status = 500 )
2024-10-28 02:20:28 +00:00
@api_view ( [ ' GET ' ] )
def get_team_info ( request , zekken_number ) :
2025-08-29 20:47:11 +09:00
# zekken_numberまたはzekken_labelで検索を試行
entry = None
try :
# まずzekken_numberで検索( 数値の場合)
if zekken_number . isdigit ( ) :
entry = Entry . objects . select_related ( ' team ' , ' event ' ) . get ( zekken_number = int ( zekken_number ) )
else :
# 数値でない場合はzekken_labelで検索
entry = Entry . objects . select_related ( ' team ' , ' event ' ) . get ( zekken_label = zekken_number )
except Entry . DoesNotExist :
# zekken_numberで見つからない場合はzekken_labelで再試行
try :
entry = Entry . objects . select_related ( ' team ' , ' event ' ) . get ( zekken_label = zekken_number )
except Entry . DoesNotExist :
# 最後にzekken_numberとして再試行( 数値以外でもエラーハンドリング)
try :
entry = Entry . objects . select_related ( ' team ' , ' event ' ) . get ( zekken_number = zekken_number )
except ( Entry . DoesNotExist , ValueError ) :
return Response ( { ' error ' : ' Entry not found ' } , status = 404 )
if not entry :
return Response ( { ' error ' : ' Entry not found ' } , status = 404 )
2024-10-28 02:20:28 +00:00
members = Member . objects . filter ( team = entry . team )
2024-10-30 08:12:31 +00:00
start_datetime = entry . event . start_datetime #イベントの規定スタート時刻
if entry . event . self_rogaining :
#get_checkinsの中で、
# start_datetime = -1(ロゲ開始)のcreate_at
logger . debug ( f " self.rogaining= { entry . event . self_rogaining } => start_datetime = -1(ロゲ開始)のcreate_at " )
2024-11-03 05:16:05 +00:00
# チームの最初のゴール時間を取得
2025-08-29 21:11:00 +09:00
logger . info ( f " Searching for GoalImages with team_name= ' { entry . team . team_name } ' , event_code= ' { entry . event . event_name } ' " )
2024-10-28 20:25:05 +00:00
goal_record = GoalImages . objects . filter (
team_name = entry . team . team_name ,
event_code = entry . event . event_name
2024-11-03 05:16:05 +00:00
) . order_by ( ' goaltime ' ) . first ( )
2024-10-28 20:25:05 +00:00
2024-11-02 23:53:34 +00:00
# Nullチェックを追加してからログ出力
goalimage_url = None
goaltime = None
if goal_record :
2025-08-29 21:11:00 +09:00
logger . info ( f " Found goal record: id= { goal_record . id } , goaltime= { goal_record . goaltime } " )
2024-11-02 23:53:34 +00:00
try :
goaltime = goal_record . goaltime
2025-08-29 21:17:59 +09:00
if goal_record . goalimage :
# S3対応のget_image_url関数を使用
goalimage_url = get_image_url ( goal_record . goalimage )
if goalimage_url :
logger . info ( f " get_team_info record.goalimage_url= { goalimage_url } " )
else :
logger . warning ( f " Failed to generate URL for goal image " )
2024-11-02 23:53:34 +00:00
else :
2025-08-29 21:11:00 +09:00
logger . info ( f " Goal record exists but no image found: goalimage= { goal_record . goalimage } " )
2024-11-02 23:53:34 +00:00
except ValueError as e :
logger . warning ( f " Error accessing goal image: { str ( e ) } " )
else :
2025-08-29 21:11:00 +09:00
logger . info ( f " No goal record found for team_name= ' { entry . team . team_name } ' , event_code= ' { entry . event . event_name } ' " )
2024-11-02 23:53:34 +00:00
2024-10-28 02:20:28 +00:00
return Response ( {
' team_name ' : entry . team . team_name ,
' members ' : ' , ' . join ( [ f " { m . lastname } { m . firstname } " for m in members ] ) ,
2024-10-28 20:25:05 +00:00
' event_code ' : entry . event . event_name ,
' start_datetime ' : entry . event . start_datetime ,
2024-11-02 23:53:34 +00:00
' end_datetime ' : goaltime , #goal_record.goaltime if goal_record else None,
' goal_photo ' : goalimage_url , #goal_record.goalimage if goal_record else None,
2024-10-30 08:12:31 +00:00
' duration ' : entry . category . duration . total_seconds ( )
2024-10-28 02:20:28 +00:00
} )
2024-10-28 20:25:05 +00:00
def create ( self , request , * args , * * kwargs ) :
logger . info ( f " Attempting to create new member for team: { self . kwargs [ ' team_id ' ] } " )
logger . debug ( f " Received data: { request . data } " )
team = Team . objects . get ( id = self . kwargs [ ' team_id ' ] )
2024-10-29 00:56:12 +00:00
def get_image_url ( image_path ) :
2025-08-24 19:44:36 +09:00
""" 画像URLを生成する補助関数 - S3とローカルメディアの両方に対応 """
2024-10-29 00:56:12 +00:00
if not image_path :
return None
2025-08-24 19:44:36 +09:00
image_path_str = str ( image_path )
# シミュレーション画像の場合はローカルメディアパスを返す
if image_path_str in [ ' simulation_image.jpg ' , ' /media/simulation_image.jpg ' ] :
return f " { settings . MEDIA_URL } simulation_image.jpg "
# ローカルメディアのフルパス(/media/で始まる) の場合は、S3パスを抽出
if image_path_str . startswith ( ' /media/ ' ) :
# /media/を削除してS3パスを取得
s3_path = image_path_str [ 7 : ] # "/media/"を除去
if hasattr ( settings , ' AWS_STORAGE_BUCKET_NAME ' ) and settings . AWS_STORAGE_BUCKET_NAME :
# S3 URLを直接生成
return f " https:// { settings . AWS_S3_CUSTOM_DOMAIN } / { s3_path } "
# S3のファイルパス( checkin/で始まる、画像拡張子を含む)かどうかを判定
if ( any ( keyword in image_path_str . lower ( ) for keyword in [ ' jpg ' , ' jpeg ' , ' png ' , ' gif ' , ' webp ' ] ) and
( ' checkin/ ' in image_path_str or not image_path_str . startswith ( ' / ' ) ) ) :
# S3 URLを生成
if hasattr ( settings , ' AWS_STORAGE_BUCKET_NAME ' ) and settings . AWS_STORAGE_BUCKET_NAME :
return f " https:// { settings . AWS_S3_CUSTOM_DOMAIN } / { image_path_str } "
2024-10-29 00:56:12 +00:00
2025-08-24 19:44:36 +09:00
# その他の場合はローカルメディアURL
return f " { settings . MEDIA_URL } { image_path_str } "
def get_standard_image_url ( event_code , image_type ) :
""" 規定画像URLを取得( S3優先、フォールバック対応) """
try :
# S3から規定画像を取得
from . services . s3_service import S3Service
s3_service = S3Service ( )
s3_url = s3_service . get_standard_image_url ( event_code , image_type )
if s3_url :
return s3_url
# S3に規定画像がない場合はデフォルト画像を返す
default_images = {
' goal ' : ' https://rogaining.sumasen.net/images/gifuRoge/asset/goal.png ' ,
' start ' : ' https://rogaining.sumasen.net/images/gifuRoge/asset/start.png ' ,
' checkpoint ' : ' https://rogaining.sumasen.net/images/gifuRoge/asset/checkpoint.png ' ,
' finish ' : ' https://rogaining.sumasen.net/images/gifuRoge/asset/finish.png ' ,
' photo_none ' : ' https://rogaining.sumasen.net/images/gifuRoge/asset/photo_none.png '
}
return default_images . get ( image_type , default_images [ ' photo_none ' ] )
except Exception as e :
logger . error ( f " Error getting standard image: { e } " )
# エラー時はデフォルト画像を返す
return ' https://rogaining.sumasen.net/images/gifuRoge/asset/photo_none.png '
2024-10-29 00:56:12 +00:00
2024-10-28 02:20:28 +00:00
@api_view ( [ ' GET ' ] )
2024-10-28 20:25:05 +00:00
def get_checkins ( request , * args , * * kwargs ) :
#def get_checkins(request, zekken_number, event_code):
try :
# イベントコードをクエリパラメータから取得
zekken_number = kwargs [ ' zekken_number ' ]
if not zekken_number :
2025-09-06 00:31:53 +09:00
logger . error ( f " GET_CHECKINS: zekken_number is required " )
2024-10-28 20:25:05 +00:00
return Response ( { " error " : " zekken_number is required " } , status = 400 )
event_code = kwargs [ ' event_code ' ]
if not event_code :
2025-09-06 00:31:53 +09:00
logger . error ( f " GET_CHECKINS: event_code is required " )
2024-10-28 20:25:05 +00:00
return Response ( { " error " : " event_code is required " } , status = 400 )
2025-09-06 00:31:53 +09:00
logger . info ( f " GET_CHECKINS: Searching for zekken= { zekken_number } , event_code= { event_code } by user { request . user . email if request . user . is_authenticated else ' Anonymous ' } " )
2025-08-29 20:05:34 +09:00
2025-08-29 21:11:00 +09:00
# チーム情報を取得
entry = None
try :
# まずzekken_numberで検索( 数値の場合)
if zekken_number . isdigit ( ) :
entry = Entry . objects . select_related ( ' team ' , ' event ' ) . get ( zekken_number = int ( zekken_number ) )
else :
# 数値でない場合はzekken_labelで検索
entry = Entry . objects . select_related ( ' team ' , ' event ' ) . get ( zekken_label = zekken_number )
except Entry . DoesNotExist :
# zekken_numberで見つからない場合はzekken_labelで再試行
try :
entry = Entry . objects . select_related ( ' team ' , ' event ' ) . get ( zekken_label = zekken_number )
except Entry . DoesNotExist :
# 最後にzekken_numberとして再試行( 数値以外でもエラーハンドリング)
try :
entry = Entry . objects . select_related ( ' team ' , ' event ' ) . get ( zekken_number = zekken_number )
except ( Entry . DoesNotExist , ValueError ) :
2025-09-06 00:31:53 +09:00
logger . warning ( f " GET_CHECKINS: Entry not found for zekken: { zekken_number } " )
2025-08-29 21:11:00 +09:00
entry = None
2024-10-28 20:25:05 +00:00
# チェックインデータの取得とCP情報の結合
checkins = GpsCheckin . objects . filter (
2025-08-29 18:09:32 +09:00
zekken = zekken_number ,
2024-10-28 20:25:05 +00:00
event_code = event_code
2025-08-29 18:09:32 +09:00
) . order_by ( ' serial_number ' )
2025-08-29 20:05:34 +09:00
2025-09-06 00:31:53 +09:00
logger . info ( f " GET_CHECKINS: Found { checkins . count ( ) } checkins for zekken { zekken_number } " )
2024-10-28 20:25:05 +00:00
data = [ ]
for c in checkins :
2025-08-30 02:20:25 +09:00
location = Location2025 . objects . filter ( cp = c . cp_number , group = event_code ) . first ( )
2025-08-29 20:05:34 +09:00
2025-08-29 21:11:00 +09:00
# チェックイン画像を取得
checkin_image = None
image_url = None
if entry and location : # entryとlocationが存在する場合のみ画像を検索
# location.cpを使用してチェックイン画像を検索
checkin_image = CheckinImages . objects . filter (
team_name = entry . team . team_name ,
event_code = event_code ,
cp_number = location . cp # location.cpを使用
) . first ( )
# 画像URLを生成
if checkin_image and checkin_image . checkinimage :
try :
2025-08-29 21:17:59 +09:00
# S3対応のget_image_url関数を使用
image_url = get_image_url ( checkin_image . checkinimage )
if image_url :
2025-08-29 21:11:00 +09:00
logger . info ( f " Found checkin image for CP { location . cp } (sub_loc_id: { location . sub_loc_id } ): { image_url } " )
2025-08-29 21:17:59 +09:00
else :
logger . warning ( f " Failed to generate URL for checkin image CP { location . cp } " )
2025-08-29 21:11:00 +09:00
except Exception as e :
logger . warning ( f " Error accessing checkin image for CP { location . cp } : { str ( e ) } " )
else :
logger . debug ( f " No checkin image found for CP { location . cp } (sub_loc_id: { location . sub_loc_id } ) " )
else :
if not entry :
logger . warning ( f " No entry found for zekken { zekken_number } , cannot search for checkin images " )
if not location :
logger . warning ( f " No location found for CP { c . cp_number } " )
2025-08-29 20:05:34 +09:00
formatted_time = None
if c . checkin_time :
try :
# checkin_timeが文字列の場合はdatetimeに変換
if isinstance ( c . checkin_time , str ) :
c . checkin_time = datetime . strptime ( c . checkin_time , ' % Y- % m- %d % H: % M: % S ' )
# JST(+9時間)に変換して時刻フォーマット
jst_time = c . checkin_time + timedelta ( hours = 9 )
formatted_time = jst_time . strftime ( ' % H: % M: % S ' )
except ( ValueError , TypeError , AttributeError ) as e :
logger . error ( f " Error formatting checkin_time for checkin { c . id } : { str ( e ) } " )
logger . error ( f " Raw checkin_time value: { c . checkin_time } " )
formatted_time = None
data . append ( {
' id ' : c . id ,
' path_order ' : c . serial_number ,
' cp_number ' : c . cp_number ,
' sub_loc_id ' : location . sub_loc_id if location else f " # { c . cp_number } " ,
' location_name ' : location . location_name if location else None ,
' create_at ' : formatted_time , #(c.checkin_time + timedelta(hours=9)).strftime('%H:%M:%S') if c.checkin_time else None,
' validate_location ' : True , # デフォルト値として設定
' points ' : location . checkin_point if location else 0 , # locationから取得
' buy_flag ' : False , # デフォルト値として設定
' photos ' : location . photos if location else None ,
2025-08-29 21:11:00 +09:00
' image_address ' : image_url , # CheckinImagesテーブルから取得した画像URL
2025-08-29 20:05:34 +09:00
' receipt_address ' : None , # 現在のモデルには存在しない
' location_name ' : location . location_name if location else None ,
' checkin_point ' : location . checkin_point if location else None ,
' buy_point ' : location . buy_point if location else 0
} )
2024-10-28 20:25:05 +00:00
2024-10-29 00:56:12 +00:00
#logger.debug(f"data={data}")
2025-09-06 00:31:53 +09:00
logger . info ( f " GET_CHECKINS: Returning { len ( data ) } checkin records for zekken { zekken_number } , event { event_code } " )
2024-10-28 20:25:05 +00:00
return Response ( data )
except Exception as e :
logger . error ( f " Error in get_checkins: { str ( e ) } " , exc_info = True )
return Response (
{ " error " : f " Failed to retrieve checkins: { str ( e ) } " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
2024-10-28 02:20:28 +00:00
2024-11-02 23:53:34 +00:00
2024-10-28 02:20:28 +00:00
@api_view ( [ ' POST ' ] )
def update_checkins ( request ) :
2024-11-08 04:30:58 +00:00
try :
with transaction . atomic ( ) :
update_base = request . data
2025-09-06 00:31:53 +09:00
logger . info ( f " UPDATE_CHECKINS: Processing update data for zekken { update_base . get ( ' zekken_number ' ) } , event { update_base . get ( ' event_code ' ) } by user { request . user . email if request . user . is_authenticated else ' Anonymous ' } " )
2024-11-08 04:30:58 +00:00
zekken_number = update_base [ ' zekken_number ' ]
event_code = update_base [ ' event_code ' ]
# 既存レコードの更新
for update in update_base [ ' checkins ' ] :
if ' id ' in update and int ( update [ ' id ' ] ) > 0 :
try :
checkin = GpsCheckin . objects . get ( id = update [ ' id ' ] )
2025-09-06 00:31:53 +09:00
logger . info ( f " UPDATE_CHECKINS: Updating existing checkin ID { update [ ' id ' ] } - CP { update . get ( ' cp_number ' ) } , validation: { update . get ( ' validation ' ) } , points: { update . get ( ' points ' ) } " )
2024-11-08 04:30:58 +00:00
# 既存レコードの更新
2025-08-29 18:09:32 +09:00
checkin . serial_number = update [ ' order ' ]
2024-11-08 04:30:58 +00:00
checkin . buy_flag = update . get ( ' buy_flag ' , False )
checkin . validate_location = update . get ( ' validation ' , False )
checkin . points = update . get ( ' points ' , 0 )
2024-11-08 14:33:46 +09:00
# checkin.update_at = timezone.now() チェックイン時刻は更新しない
2024-11-08 04:30:58 +00:00
checkin . update_user = request . user . email if request . user . is_authenticated else None
checkin . save ( )
2025-09-06 00:31:53 +09:00
logger . info ( f " UPDATE_CHECKINS: Successfully updated checkin ID { checkin . id } for zekken { zekken_number } " )
2024-11-08 04:30:58 +00:00
except GpsCheckin . DoesNotExist :
2025-09-06 00:31:53 +09:00
logger . error ( f " UPDATE_CHECKINS: Checkin with id { update [ ' id ' ] } not found for zekken { zekken_number } " )
2024-11-08 04:30:58 +00:00
continue # エラーを無視して次のレコードの処理を継続
# 新規レコードの作成
for update in update_base [ ' checkins ' ] :
if ' id ' in update and int ( update [ ' id ' ] ) == 0 :
2025-09-06 00:31:53 +09:00
logger . info ( f " UPDATE_CHECKINS: Creating new checkin for CP { update . get ( ' cp_number ' ) } , validation: { update . get ( ' validation ' ) } , points: { update . get ( ' points ' ) } " )
2024-11-08 04:30:58 +00:00
try :
checkin = GpsCheckin . objects . create (
2025-08-29 18:09:32 +09:00
zekken = zekken_number ,
2024-11-08 04:30:58 +00:00
event_code = event_code ,
2025-08-29 18:09:32 +09:00
serial_number = update [ ' order ' ] ,
2024-11-08 04:30:58 +00:00
cp_number = update [ ' cp_number ' ] ,
validate_location = update . get ( ' validation ' , False ) ,
buy_flag = update . get ( ' buy_flag ' , False ) ,
points = update . get ( ' points ' , 0 ) ,
2024-11-08 14:33:46 +09:00
# create_at=timezone.now(), チェックイン時刻は更新しない
2024-11-08 04:30:58 +00:00
update_at = timezone . now ( ) ,
create_user = request . user . email if request . user . is_authenticated else None ,
update_user = request . user . email if request . user . is_authenticated else None
)
2025-09-06 00:31:53 +09:00
logger . info ( f " UPDATE_CHECKINS: Successfully created new checkin ID { checkin . id } for zekken { zekken_number } , CP { checkin . cp_number } " )
2024-11-08 04:30:58 +00:00
except Exception as e :
2025-09-06 00:31:53 +09:00
logger . error ( f " UPDATE_CHECKINS: Error creating new checkin for zekken { zekken_number } : { str ( e ) } " )
2024-11-08 04:30:58 +00:00
continue # エラーを無視して次のレコードの処理を継続
# 更新後のデータを順序付けて取得
updated_checkins = GpsCheckin . objects . filter (
2025-08-29 18:09:32 +09:00
zekken = zekken_number ,
2024-11-08 04:30:58 +00:00
event_code = event_code
2025-08-29 18:09:32 +09:00
) . order_by ( ' serial_number ' )
2024-11-08 04:30:58 +00:00
return Response ( {
' status ' : ' success ' ,
' message ' : ' Checkins updated successfully ' ,
2025-08-29 18:09:32 +09:00
' data ' : [ { ' id ' : c . id , ' path_order ' : c . serial_number } for c in updated_checkins ]
2024-11-08 04:30:58 +00:00
} )
except Exception as e :
logger . error ( f " Error in update_checkins: { str ( e ) } " , exc_info = True )
return Response (
{ " error " : " Failed to update checkins " , " detail " : str ( e ) } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
@api_view ( [ ' POST ' ] )
def update_checkins_old ( request ) :
2024-11-02 23:53:34 +00:00
try :
with transaction . atomic ( ) :
update_base = request . data
2025-09-06 00:31:53 +09:00
logger . info ( f " UPDATE_CHECKINS_OLD: Processing update data for zekken { update_base . get ( ' zekken_number ' ) } , event { update_base . get ( ' event_code ' ) } by user { request . user . email if request . user . is_authenticated else ' Anonymous ' } " )
2024-11-02 23:53:34 +00:00
zekken_number = update_base [ ' zekken_number ' ]
event_code = update_base [ ' event_code ' ]
for update in update_base [ ' checkins ' ] :
if ' id ' in update and int ( update [ ' id ' ] ) > 0 :
# 既存レコードの更新
2025-09-06 00:31:53 +09:00
logger . info ( f " UPDATE_CHECKINS_OLD: Updating existing checkin ID { update [ ' id ' ] } - CP { update . get ( ' cp_number ' ) } " )
2024-11-02 23:53:34 +00:00
try :
checkin = GpsCheckin . objects . get ( id = update [ ' id ' ] )
# 既存レコードの更新
2025-08-29 18:09:32 +09:00
checkin . serial_number = update [ ' order ' ]
2024-11-02 23:53:34 +00:00
checkin . buy_flag = update . get ( ' buy_flag ' , False )
checkin . validate_location = update . get ( ' validation ' , False )
checkin . points = update . get ( ' points ' , 0 )
checkin . save ( )
2025-09-06 00:31:53 +09:00
logger . info ( f " UPDATE_CHECKINS_OLD: Successfully updated checkin ID { checkin . id } for zekken { zekken_number } " )
2024-11-02 23:53:34 +00:00
except GpsCheckin . DoesNotExist :
2025-09-06 00:31:53 +09:00
logger . error ( f " UPDATE_CHECKINS_OLD: Checkin with id { update [ ' id ' ] } not found for zekken { zekken_number } " )
2024-11-02 23:53:34 +00:00
return Response (
{ " error " : f " Checkin with id { update [ ' id ' ] } not found " } ,
status = status . HTTP_404_NOT_FOUND
)
for update in update_base [ ' checkins ' ] :
if ' id ' in update and int ( update [ ' id ' ] ) == 0 :
# 新規レコードの作成
2025-09-06 00:31:53 +09:00
logger . info ( f " UPDATE_CHECKINS_OLD: Creating new checkin for CP { update . get ( ' cp_number ' ) } , validation: { update . get ( ' validation ' ) } " )
2024-11-02 23:53:34 +00:00
try :
checkin = GpsCheckin . objects . create (
2025-08-29 18:09:32 +09:00
zekken = update_base [ ' zekken_number ' ] ,
2024-11-02 23:53:34 +00:00
event_code = update_base [ ' event_code ' ] ,
2025-08-29 18:09:32 +09:00
serial_number = update [ ' order ' ] ,
2024-11-02 23:53:34 +00:00
cp_number = update [ ' cp_number ' ] ,
validate_location = update . get ( ' validation ' , False ) ,
buy_flag = update . get ( ' buy_flag ' , False ) ,
points = update . get ( ' points ' , 0 ) ,
create_at = timezone . now ( ) ,
update_at = timezone . now ( ) ,
create_user = request . user . email if request . user . is_authenticated else None ,
update_user = request . user . email if request . user . is_authenticated else None
)
logger . info ( f " Updated existing checkin result: { checkin } " )
except KeyError as e :
logger . error ( f " Missing required field: { str ( e ) } " )
return Response (
{ " error " : f " Missing required field: { str ( e ) } " } ,
status = status . HTTP_400_BAD_REQUEST
)
return Response ( { ' status ' : ' success ' , ' message ' : ' Checkins updated successfully ' } )
except Exception as e :
logger . error ( f " Error in update_checkins: { str ( e ) } " , exc_info = True )
return Response (
{ " error " : " Failed to update checkins " , " detail " : str ( e ) } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
2024-10-28 02:20:28 +00:00
2024-11-11 15:11:21 +09:00
@api_view ( [ ' GET ' ] )
2024-11-11 15:41:32 +09:00
def get_photo_list ( request ) :
2024-11-11 15:11:21 +09:00
print ( " /get_photo_list " )
2024-11-11 15:41:32 +09:00
zekken = request . GET . get ( ' zekken ' )
event = request . GET . get ( ' event ' )
if not zekken or not event :
return JsonResponse ( {
" status " : " ERROR " ,
" message " : " Missing required parameters: zekken and event "
} , status = 400 )
2024-11-11 15:58:05 +09:00
# イベントコードからダブルクォートを削除
event = event . strip ( ' " ' )
zekken = zekken . strip ( ' " ' )
2024-11-11 15:11:21 +09:00
zekken_conversion = {
" MF3-160 " : " MZ3-160 " ,
" MF3-161 " : " MZ3-161 " ,
" MF3-162 " : " MZ3-162 " ,
" MF3-163 " : " MZ3-163 " ,
" MF5-170 " : " MZ5-170 " ,
" MF5-171 " : " MZ5-171 " ,
" MF5-172 " : " MZ5-172 " ,
" MF5-173 " : " MZ5-173 " ,
" MF5-174 " : " MZ5-174 " ,
" MF5-175 " : " MZ5-175 " ,
" MF5-176 " : " MZ5-176 " ,
" MF5-177 " : " MZ5-177 " ,
" MF5-178 " : " MZ5-178 " ,
" MF5-179 " : " MZ5-179 "
}
if zekken in zekken_conversion :
zekken = zekken_conversion [ zekken ]
photo_list = [ ]
2024-11-11 15:46:47 +09:00
print ( f " Final photo_list length: { len ( photo_list ) } " )
2024-11-11 15:28:43 +09:00
photo_list . extend ( _get_photo_list ( zekken , event ) )
2024-11-11 15:11:21 +09:00
return JsonResponse ( {
" status " : " OK " ,
" photo_list " : photo_list ,
2024-11-11 15:28:43 +09:00
" report " : _get_final_report ( zekken , event )
2024-11-11 15:11:21 +09:00
} )
2024-11-11 15:41:32 +09:00
def _get_final_report ( zekken , event_code ) :
2024-11-11 15:11:21 +09:00
report = f " https://sumasenrogaining.s3.us-west-2.amazonaws.com/ { event_code } /scoreboard/certificate_ { zekken } .pdf "
return report
2024-11-11 15:41:32 +09:00
def _get_photo_list ( zekken , event_code ) :
2024-11-11 15:11:21 +09:00
photos = [ ]
try :
2025-08-29 16:38:01 +09:00
print ( f " Query parameters - zekken: { zekken } , event_code: { event_code } " )
2024-11-11 15:46:47 +09:00
2025-08-29 16:38:01 +09:00
checkins = GpsCheckin . objects . filter ( zekken = zekken , event_code = event_code )
2024-11-11 15:46:47 +09:00
# クエリ結果の件数を出力
print ( f " Found { checkins . count ( ) } checkins " )
print ( f " SQL Query: { checkins . query } " )
2024-11-11 15:11:21 +09:00
for checkin in checkins :
2024-11-11 15:46:47 +09:00
print ( f " Processing checkin - CP number: { checkin . cp_number } " )
2024-11-11 15:58:05 +09:00
if checkin . image_address :
2024-11-11 16:02:02 +09:00
# URLオブジェクトかどうかを判定
2024-11-11 15:58:05 +09:00
if hasattr ( checkin . image_address , ' url ' ) :
photo_url = checkin . image_address . url
else :
2024-11-11 16:02:02 +09:00
# 文字列の場合、httpで始まるかどうかを確認
image_address_str = str ( checkin . image_address )
if image_address_str . lower ( ) . startswith ( ' http ' ) :
photo_url = image_address_str
else :
photo_url = f " http://rogaining.sumasen.net/media/ { image_address_str } "
2024-11-11 15:58:05 +09:00
print ( f " Found image for CP { checkin . cp_number } : { photo_url } " )
photos . append ( {
" cp_number " : checkin . cp_number ,
" photo_url " : photo_url
} )
else :
print ( f " No image found for CP { checkin . cp_number } " )
2024-11-11 16:02:02 +09:00
print ( f " Total photos found: { len ( photos ) } " )
2024-11-11 15:58:05 +09:00
2024-11-11 15:11:21 +09:00
except Exception as e :
print ( f " Error in _get_photo_list: { str ( e ) } " )
return photos
2024-11-08 04:30:58 +00:00
2024-10-28 02:20:28 +00:00
@api_view ( [ ' GET ' ] )
2024-11-08 04:30:58 +00:00
def export_excel ( request , zekken_number , event_code ) :
temp_dir = None
2024-11-08 14:47:10 +00:00
2024-11-08 04:30:58 +00:00
try :
# パラメータを文字列型に変換
zekken_number = str ( zekken_number )
event_code = str ( event_code )
logger . info ( f " Exporting Excel/PDF for zekken_number: { zekken_number } , event_code: { event_code } " )
# 入力値の検証
if not zekken_number or not event_code :
logger . error ( " Missing required parameters " )
return Response (
{ " error " : " Both zekken_number and event_code are required " } ,
status = status . HTTP_400_BAD_REQUEST
)
# docbaseディレクトリのパスを絶対パスで設定
base_dir = os . path . dirname ( os . path . dirname ( os . path . abspath ( __file__ ) ) )
docbase_path = os . path . join ( base_dir , ' docbase ' )
# ディレクトリ存在確認と作成
os . makedirs ( docbase_path , exist_ok = True )
# 設定ファイルのパス
template_path = os . path . join ( docbase_path , ' certificate_template.xlsx ' )
ini_path = os . path . join ( docbase_path , ' certificate.ini ' )
# テンプレートと設定ファイルの存在確認
if not os . path . exists ( template_path ) :
logger . error ( f " Template file not found: { template_path } " )
return Response (
{ " error " : " Excel template file missing " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
if not os . path . exists ( ini_path ) :
logger . error ( f " INI file not found: { ini_path } " )
return Response (
{ " error " : " Configuration file missing " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
# Docker環境用のデータベース設定を使用
db_settings = settings . DATABASES [ ' default ' ]
# 初期化
variables = {
" zekken_number " : str ( zekken_number ) ,
" event_code " : str ( event_code ) ,
" db " : str ( db_settings [ ' NAME ' ] ) ,
" username " : str ( db_settings [ ' USER ' ] ) ,
" password " : str ( db_settings [ ' PASSWORD ' ] ) ,
" host " : str ( db_settings [ ' HOST ' ] ) ,
" port " : str ( db_settings [ ' PORT ' ] ) ,
" template_path " : template_path
}
try :
excel = SumasenExcel ( document = " certificate " , variables = variables , docbase = docbase_path )
ret = excel . make_report ( variables = variables )
if ret [ " status " ] != True :
message = ret . get ( " message " , " No message provided " )
logger . error ( f " Excelファイル作成失敗 : ret.message= { message } " )
return Response (
{ " error " : f " Excel generation failed: { message } " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
excel_path = ret . get ( " filepath " )
if not excel_path or not os . path . exists ( excel_path ) :
logger . error ( f " Output file not found: { excel_path } " )
return Response (
{ " error " : " Generated file not found " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
2024-11-09 09:50:58 +00:00
# 一時ディレクトリを作成( ASCII文字のみのパスを使用)
temp_dir = tempfile . mkdtemp ( prefix = ' lo- ' )
logger . debug ( f " Created temp directory: { temp_dir } " )
# ASCII文字のみの作業ディレクトリを作成
work_dir = os . path . join ( temp_dir , ' work ' )
output_dir = os . path . join ( temp_dir , ' output ' )
os . makedirs ( work_dir , exist_ok = True )
os . makedirs ( output_dir , exist_ok = True )
# すべてのディレクトリに適切な権限を設定
for directory in [ temp_dir , work_dir , output_dir ] :
os . chmod ( directory , 0o777 )
logger . debug ( f " Set permissions for directory: { directory } " )
# ASCII文字のみの一時ファイル名を使用
temp_excel_name = f " certificate_ { zekken_number } .xlsx "
temp_excel_path = os . path . join ( work_dir , temp_excel_name )
# 元のExcelファイルを作業ディレクトリにコピー
shutil . copy2 ( excel_path , temp_excel_path )
os . chmod ( temp_excel_path , 0o666 )
logger . debug ( f " Copied Excel file to: { temp_excel_path } " )
# LibreOffice設定ディレクトリを作成
libreoffice_config_dir = os . path . join ( temp_dir , ' libreoffice ' )
os . makedirs ( libreoffice_config_dir , exist_ok = True )
# フォント設定ディレクトリを作成
font_conf_dir = os . path . join ( temp_dir , ' fonts ' )
os . makedirs ( font_conf_dir , exist_ok = True )
# フォント設定ファイルを作成
fonts_conf_content = ''' <?xml version= " 1.0 " ?>
< ! DOCTYPE fontconfig SYSTEM " fonts.dtd " >
< fontconfig >
< dir > / usr / share / fonts < / dir >
< match target = " pattern " >
< test qual = " any " name = " family " >
< string > sans - serif < / string >
< / test >
< edit name = " family " mode = " assign " binding = " same " >
< string > IPAexGothic < / string >
< / edit >
< / match >
< match target = " pattern " >
< test qual = " any " name = " family " >
< string > serif < / string >
< / test >
< edit name = " family " mode = " assign " binding = " same " >
< string > IPAexMincho < / string >
< / edit >
< / match >
< / fontconfig > '''
font_conf_path = os . path . join ( libreoffice_config_dir , ' fonts.conf ' )
with open ( font_conf_path , ' w ' ) as f :
f . write ( fonts_conf_content )
# LibreOfficeのフォント設定を作成
registry_dir = os . path . join ( libreoffice_config_dir , ' registry ' )
os . makedirs ( registry_dir , exist_ok = True )
# フォント埋め込み設定を作成
pdf_export_config = ''' <?xml version= " 1.0 " encoding= " UTF-8 " ?>
< oor : data xmlns : oor = " http://openoffice.org/2001/registry " >
< oor : component - data oor : package = " org.openoffice.Office.Common " oor : name = " Filter " >
< node oor : name = " PDF " >
< prop oor : name = " EmbedFonts " oor : op = " fuse " >
< value > true < / value >
< / prop >
< prop oor : name = " ExportFormFields " oor : op = " fuse " >
< value > true < / value >
< / prop >
< prop oor : name = " UseTaggedPDF " oor : op = " fuse " >
< value > true < / value >
< / prop >
< / node >
< / oor : component - data >
< / oor : data > '''
pdf_config_path = os . path . join ( registry_dir , ' pdf_export.xcu ' )
with open ( pdf_config_path , ' w ' ) as f :
f . write ( pdf_export_config )
# すべてのディレクトリに適切な権限を設定
for directory in [ temp_dir , work_dir , output_dir , registry_dir ] :
os . chmod ( directory , 0o777 )
logger . debug ( f " Set permissions for directory: { directory } " )
os . chmod ( temp_excel_path , 0o666 )
os . chmod ( font_conf_path , 0o666 )
os . chmod ( pdf_config_path , 0o666 )
2024-11-08 04:30:58 +00:00
# フォーマット指定( excel or pdf)
format_type = request . query_params . get ( ' format ' , ' pdf ' )
if format_type . lower ( ) == ' pdf ' :
try :
2024-11-08 18:42:07 +09:00
# パスとファイル名に分離
2024-11-09 09:50:58 +00:00
file_dir = os . path . dirname ( temp_excel_path ) # パス部分の取得
file_name = os . path . basename ( temp_excel_path ) # ファイル名部分の取得
2024-11-08 04:30:58 +00:00
2024-11-08 18:42:07 +09:00
# ファイル名の拡張子をpdfに変更
base_name = os . path . splitext ( file_name ) [ 0 ] # 拡張子を除いたファイル名
new_file_name = base_name + " .pdf " # 新しい拡張子を追加
2024-11-08 04:30:58 +00:00
2024-11-08 18:42:07 +09:00
# 新しいパスを作成
pdf_path = os . path . join ( file_dir , new_file_name )
2024-11-08 04:30:58 +00:00
2024-11-08 18:42:07 +09:00
logger . info ( f " Converting Excel to PDF in directory: { file_dir } " )
2024-11-08 04:30:58 +00:00
# LibreOfficeを使用してExcelをPDFに変換
conversion_command = [
' soffice ' , # LibreOfficeの代替コマンド
' --headless ' ,
2024-11-09 09:50:58 +00:00
' --convert-to ' , ' pdf:writer_pdf_Export ' ,
' --outdir ' , file_dir ,
' -env:UserInstallation=file:// ' + libreoffice_config_dir ,
temp_excel_path
2024-11-08 04:30:58 +00:00
]
logger . debug ( f " Running conversion command: { ' ' . join ( conversion_command ) } " )
# 環境変数を設定
env = os . environ . copy ( )
2024-11-08 14:47:10 +00:00
#env['HOME'] = temp_dir # LibreOfficeの設定ディレクトリを一時ディレクトリに設定
2024-11-09 09:50:58 +00:00
env [ ' HOME ' ] = temp_dir
env [ ' LANG ' ] = ' ja_JP.UTF-8 ' # 日本語環境を設定
env [ ' LC_ALL ' ] = ' ja_JP.UTF-8 '
env [ ' FONTCONFIG_FILE ' ] = font_conf_path
env [ ' FONTCONFIG_PATH ' ] = font_conf_dir
2024-11-08 04:30:58 +00:00
# 変換プロセスを実行
process = subprocess . run (
conversion_command ,
env = env ,
capture_output = True ,
text = True ,
2024-11-09 09:50:58 +00:00
cwd = work_dir ,
check = True ,
timeout = 30
2024-11-08 04:30:58 +00:00
)
logger . debug ( f " Conversion output: { process . stdout } " )
# PDFファイルの存在確認
2024-11-08 18:42:07 +09:00
if not os . path . exists ( pdf_path ) :
2024-11-08 04:30:58 +00:00
logger . error ( " PDF conversion failed - output file not found " )
return Response (
{ " error " : " PDF conversion failed - output file not found " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
2025-09-03 21:56:40 +09:00
s3 = rog_utils_module . S3Bucket ( ' sumasenrogaining ' )
2024-11-09 09:50:58 +00:00
s3 . upload_file ( pdf_path , f ' { event_code } /scoreboard/certificate_ { zekken_number } .pdf ' )
s3 . upload_file ( excel_path , f ' { event_code } /scoreboard_excel/certificate_ { zekken_number } .xlsx ' )
2024-11-08 23:43:31 +09:00
2025-08-24 19:44:36 +09:00
# PDFファイルを読み込んでレスポンスとして返す
with open ( pdf_path , ' rb ' ) as pdf_file :
pdf_content = pdf_file . read ( )
2024-11-09 09:50:58 +00:00
os . remove ( temp_excel_path )
2024-11-08 23:43:31 +09:00
os . remove ( excel_path )
os . remove ( pdf_path )
2025-08-24 19:44:36 +09:00
# PDFファイルをレスポンスとして返す
response = HttpResponse (
pdf_content ,
content_type = ' application/pdf '
)
response [ ' Content-Disposition ' ] = f ' inline; filename= " certificate_ { zekken_number } _ { event_code } .pdf " '
return response
2024-11-08 04:30:58 +00:00
except subprocess . CalledProcessError as e :
logger . error ( f " Error converting to PDF: { str ( e ) } \n STDOUT: { e . stdout } \n STDERR: { e . stderr } " )
return Response (
{ " error " : f " PDF conversion failed: { str ( e ) } " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
2024-11-09 09:50:58 +00:00
)
2024-11-08 04:30:58 +00:00
finally :
# 一時ディレクトリの削除
if temp_dir and os . path . exists ( temp_dir ) :
try :
shutil . rmtree ( temp_dir )
logger . debug ( f " Temporary directory removed: { temp_dir } " )
except Exception as e :
logger . warning ( f " Failed to remove temporary directory: { str ( e ) } " )
else : # Excel形式の場合
with open ( excel_path , ' rb ' ) as excel_file :
response = HttpResponse (
excel_file . read ( ) ,
content_type = ' application/vnd.openxmlformats-officedocument.spreadsheetml.sheet '
)
response [ ' Content-Disposition ' ] = f ' attachment; filename= " certificate_ { zekken_number } _ { event_code } .xlsx " '
return response
except Exception as e :
logger . error ( f " Error in Excel/PDF generation: { str ( e ) } " , exc_info = True )
return Response (
{ " error " : f " File generation failed: { str ( e ) } " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
except Exception as e :
logger . error ( f " Error in export_excel: { str ( e ) } " , exc_info = True )
return Response (
{ " error " : " Failed to export file " , " detail " : str ( e ) } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
finally :
# 確実に一時ディレクトリを削除
if temp_dir and os . path . exists ( temp_dir ) :
try :
shutil . rmtree ( temp_dir )
except Exception as e :
logger . warning ( f " Failed to remove temporary directory in finally block: { str ( e ) } " )
@api_view ( [ ' GET ' ] )
def export_exceli_old2 ( request , zekken_number , event_code ) :
try :
# パラメータを文字列型に変換
zekken_number = str ( zekken_number )
event_code = str ( event_code )
logger . info ( f " Exporting Excel for zekken_number: { zekken_number } , event_code: { event_code } " )
# 入力値の検証
if not zekken_number or not event_code :
logger . error ( " Missing required parameters " )
return Response (
{ " error " : " Both zekken_number and event_code are required " } ,
status = status . HTTP_400_BAD_REQUEST
)
# docbaseディレクトリのパスを絶対パスで設定
base_dir = os . path . dirname ( os . path . dirname ( os . path . abspath ( __file__ ) ) )
docbase_path = os . path . join ( base_dir , ' docbase ' )
# ディレクトリ存在確認と作成
os . makedirs ( docbase_path , exist_ok = True )
# 設定ファイルのパス
template_path = os . path . join ( docbase_path , ' certificate_template.xlsx ' )
ini_path = os . path . join ( docbase_path , ' certificate.ini ' )
# テンプレートと設定ファイルの存在確認
if not os . path . exists ( template_path ) :
logger . error ( f " Template file not found: { template_path } " )
return Response (
{ " error " : " Excel template file missing " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
if not os . path . exists ( ini_path ) :
logger . error ( f " INI file not found: { ini_path } " )
return Response (
{ " error " : " Configuration file missing " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
# Docker環境用のデータベース設定を使用
db_settings = settings . DATABASES [ ' default ' ]
2024-10-28 02:20:28 +00:00
2024-11-08 04:30:58 +00:00
# 初期化
variables = {
" zekken_number " : str ( zekken_number ) ,
" event_code " : str ( event_code ) ,
" db " : str ( db_settings [ ' NAME ' ] ) , #"rogdb",
" username " : str ( db_settings [ ' USER ' ] ) , #"admin",
" password " : str ( db_settings [ ' PASSWORD ' ] ) , #"admin123456",
" host " : str ( db_settings [ ' HOST ' ] ) , # Docker Composeのサービス名を使用 # "localhost",
" port " : str ( db_settings [ ' PORT ' ] ) , #"5432",
" template_path " : template_path
}
# データベース接続情報のログ出力(パスワードは除く)
logger . info ( f " Attempting database connection to { variables [ ' host ' ] } : { variables [ ' port ' ] } "
f " with user { variables [ ' username ' ] } and database { variables [ ' db ' ] } " )
try :
excel = SumasenExcel ( document = " certificate " , variables = variables , docbase = docbase_path )
# ./docbase/certificate.ini の定義をベースに、
# ./docbase/certificate_template.xlsxを読み込み
# ./docbase/certificate_(zekken_number).xlsxを作成する
# シート初期化
logger . info ( " Generating report with variables: %s " ,
{ k : v for k , v in variables . items ( ) if k != ' password ' } ) # パスワードを除外
ret = excel . make_report ( variables = variables )
if ret [ " status " ] == True :
filepath = ret [ " filepath " ]
logging . info ( f " Excelファイル作成 : ret.filepath= { filepath } " )
else :
message = ret . get ( " message " , " No message provided " )
logging . error ( f " Excelファイル作成失敗 : ret.message= { message } " )
output_path = ret . get ( " filepath " )
if not output_path or not os . path . exists ( output_path ) :
logger . error ( f " Output file not found: { output_path } " )
return Response (
{ " error " : " Generated file not found " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
excel_path = output_path
# PDFのファイル名を生成
pdf_filename = f ' certificate_ { zekken_number } _ { event_code } .pdf '
pdf_path = os . path . join ( docbase_path , pdf_filename )
# フォーマット指定( excel or pdf)
format_type = request . query_params . get ( ' format ' , ' pdf ' ) # 'excel'
if format_type . lower ( ) == ' pdf ' :
try :
# 一時ディレクトリを作成
temp_dir = tempfile . mkdtemp ( )
temp_excel = os . path . join ( temp_dir , f ' certificate_ { zekken_number } .xlsx ' )
temp_pdf = os . path . join ( temp_dir , f ' certificate_ { zekken_number } .pdf ' )
# Excelファイルを一時ディレクトリにコピー
shutil . copy2 ( excel_path , temp_excel )
# 一時ディレクトリのパーミッションを設定
os . chmod ( temp_dir , 0o777 )
os . chmod ( temp_excel , 0o666 )
logger . info ( f " Converting Excel to PDF in temp directory: { temp_dir } " )
# LibreOfficeを使用してExcelをPDFに変換
conversion_command = [
' soffice ' ,
' --headless ' ,
' --convert-to ' ,
' pdf ' ,
' --outdir ' ,
temp_dir ,
temp_excel
]
logger . debug ( f " Running conversion command: { ' ' . join ( conversion_command ) } " )
# 環境変数を設定
env = os . environ . copy ( )
env [ ' HOME ' ] = temp_dir # LibreOfficeの設定ディレクトリを一時ディレクトリに設定
# 変換プロセスを実行
process = subprocess . run (
conversion_command ,
env = env ,
capture_output = True ,
text = True ,
check = True
)
logger . debug ( f " Conversion output: { process . stdout } " )
# PDFファイルの存在確認
if not os . path . exists ( temp_pdf ) :
logger . error ( " PDF conversion failed - output file not found " )
return Response (
{ " error " : " PDF conversion failed - output file not found " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
# PDFファイルを読み込んでレスポンスを返す
with open ( temp_pdf , ' rb ' ) as pdf_file :
pdf_content = pdf_file . read ( )
response = HttpResponse ( pdf_content , content_type = ' application/pdf ' )
response [ ' Content-Disposition ' ] = f ' attachment; filename= " certificate_ { zekken_number } _ { event_code } .pdf " '
return response
except subprocess . CalledProcessError as e :
logger . error ( f " Error converting to PDF: { str ( e ) } \n STDOUT: { e . stdout } \n STDERR: { e . stderr } " )
return Response (
{ " error " : " PDF conversion failed " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
finally :
# 一時ディレクトリの削除
if temp_dir and os . path . exists ( temp_dir ) :
try :
shutil . rmtree ( temp_dir )
logger . debug ( f " Temporary directory removed: { temp_dir } " )
except Exception as e :
logger . warning ( f " Failed to remove temporary directory: { str ( e ) } " )
else : # Excel形式の場合
with open ( excel_path , ' rb ' ) as excel_file :
response = HttpResponse (
excel_file . read ( ) ,
content_type = ' application/vnd.openxmlformats-officedocument.spreadsheetml.sheet '
)
excel_filename = f ' certificate_ { zekken_number } _ { event_code } .xlsx '
response [ ' Content-Disposition ' ] = f ' attachment; filename= " { excel_filename } " '
return response
except Exception as e :
logger . error ( f " Error in Excel/PDF generation: { str ( e ) } " , exc_info = True )
return Response (
{ " error " : f " File generation failed: { str ( e ) } " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
except Exception as e :
logger . error ( f " Error in export_excel: { str ( e ) } " , exc_info = True )
return Response (
{ " error " : " Failed to export file " , " detail " : str ( e ) } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
finally :
# 確実に一時ディレクトリを削除
if temp_dir and os . path . exists ( temp_dir ) :
try :
shutil . rmtree ( temp_dir )
except Exception as e :
logger . warning ( f " Failed to remove temporary directory in finally block: { str ( e ) } " )
2024-10-28 02:20:28 +00:00
# ----- for Supervisor -----
2024-10-28 20:25:05 +00:00
@api_view ( [ ' GET ' ] )
def test_api ( request ) :
logger . debug ( " Test API endpoint called " )
return JsonResponse ( { " status " : " API is working " } )
2024-10-28 02:20:28 +00:00
2024-11-02 23:53:34 +00:00
@api_view ( [ ' GET ' ] )
#@authentication_classes([TokenAuthentication])
#@permission_classes([IsAuthenticated])
def get_goalimage ( request ) :
"""
ゼッケン番号とイベントコードに基づいてゴール画像情報を取得するエンドポイント
Parameters :
zekken_number ( str ) : ゼッケン番号
event_code ( str ) : イベントコード
Returns :
Response : ゴール画像情報を含むJSONレスポンス
"""
try :
logger . debug ( f " get_goalimage called with params: { request . GET } " )
# リクエストパラメータを取得
zekken_number = request . GET . get ( ' zekken_number ' )
event_code = request . GET . get ( ' event_code ' )
logger . debug ( f " Searching for goal records with zekken_number= { zekken_number } , event_code= { event_code } " )
# パラメータの検証
if not zekken_number or not event_code :
return Response (
{ " error " : " zekken_number and event_code are required " } ,
status = status . HTTP_400_BAD_REQUEST
)
# ゴール画像レコードを検索(最も早いゴール時間のレコードを取得)
goal_records = GoalImages . objects . filter (
zekken_number = zekken_number ,
event_code = event_code
) . order_by ( ' goaltime ' )
logger . debug ( f " Found { goal_records . count ( ) } goal records " )
if not goal_records . exists ( ) :
return Response (
{ " message " : " No goal records found " } ,
status = status . HTTP_404_NOT_FOUND
)
# 最も早いゴール時間のレコード( cp_number = 0) を探す
valid_goal = goal_records . filter ( cp_number = 0 ) . first ( )
if not valid_goal :
return Response (
{ " message " : " No valid goal record found " } ,
status = status . HTTP_404_NOT_FOUND
)
# シリアライザでデータを整形
serializer = GolaImageSerializer ( valid_goal )
# レスポンスデータの構築
response_data = {
" goal_record " : serializer . data ,
" total_records " : goal_records . count ( ) ,
" has_multiple_records " : goal_records . count ( ) > 1
}
logger . info ( f " Retrieved goal record for zekken_number { zekken_number } in event { event_code } " )
return Response ( response_data )
except Exception as e :
logger . error ( f " Error retrieving goal record: { str ( e ) } " , exc_info = True )
return Response (
{ " error " : f " Failed to retrieve goal record: { str ( e ) } " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
@api_view ( [ ' POST ' ] )
#@authentication_classes([TokenAuthentication])
#@permission_classes([IsAuthenticated])
def update_goal_time ( request ) :
try :
logger . info ( f " update_goal_time: { request } " )
# リクエストからデータを取得
zekken_number = request . data . get ( ' zekken_number ' )
event_code = request . data . get ( ' event_code ' )
team_name = request . data . get ( ' team_name ' )
goal_time_str = request . data . get ( ' goaltime ' )
logger . info ( f " zekken_number= { zekken_number } ,event_code= { event_code } ,team_name= { team_name } ,goal_time= { goal_time_str } " )
# 入力バリデーション
#if not all([zekken_number, event_code, team_name, goal_time_str]):
# return Response(
# {"error": "Missing required fields"},
# status=status.HTTP_400_BAD_REQUEST
# )
try :
# 文字列からdatetimeオブジェクトに変換
goal_time = datetime . strptime ( goal_time_str , ' % Y- % m- %d T % H: % M: % S ' )
except ValueError :
return Response (
{ " error " : " Invalid goal time format " } ,
status = status . HTTP_400_BAD_REQUEST
)
# 既存のゴール記録を探す
goal_record = GoalImages . objects . filter (
team_name = team_name ,
event_code = event_code
) . first ( )
if goal_record :
# 既存の記録を更新
goal_record . goaltime = goal_time
2024-11-08 14:52:31 +09:00
goal_record . zekken_number = zekken_number
goal_record . cp_number = - 2
2024-11-02 23:53:34 +00:00
goal_record . save ( )
2024-11-03 05:16:05 +00:00
logger . info ( f " Updated goal time as { goal_time } for team { team_name } in event { event_code } " )
2024-11-02 23:53:34 +00:00
else :
# 新しい記録を作成
entry = Entry . objects . get ( zekken_number = zekken_number , event__event_name = event_code )
GoalImages . objects . create (
user = entry . owner ,
goaltime = goal_time ,
team_name = team_name ,
event_code = event_code ,
2024-11-08 14:52:31 +09:00
cp_number = - 2 , # ゴール地点を表すCP番号
zekken_number = zekken_number
2024-11-02 23:53:34 +00:00
)
logger . info ( f " Created new goal time record for team { team_name } in event { event_code } " )
return Response ( { " message " : " Goal time updated successfully " } )
except Entry . DoesNotExist :
logger . error ( f " Entry not found for zekken_number { zekken_number } in event { event_code } " )
return Response (
{ " error " : " Entry not found " } ,
status = status . HTTP_404_NOT_FOUND
)
except Exception as e :
logger . error ( f " Error updating goal time: { str ( e ) } " )
return Response (
{ " error " : f " Failed to update goal time: { str ( e ) } " } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
2024-11-12 07:19:18 +09:00
def get_team_status ( last_checkin_time , goal_time , event_end_time ) :
"""
チームの状態を判定する
"""
now = timezone . now ( )
if goal_time :
return " ゴール "
if not last_checkin_time :
if now > event_end_time + timedelta ( minutes = 30 ) :
return " 棄権 "
return " 競技中 "
# 最終チェックインから30分以上経過
if now > last_checkin_time + timedelta ( minutes = 30 ) :
return " 棄権 "
return " 競技中 "
def calculate_late_points ( goal_time , event_end_time ) :
""" 遅刻による減点を計算する """
if not goal_time or not event_end_time :
return 0
minutes_late = max ( 0 , int ( ( goal_time - event_end_time ) . total_seconds ( ) / 60 ) )
return minutes_late * - 50
def is_disqualified ( start_time , goal_time , duration ) :
""" 失格判定を行う """
if not goal_time or not start_time or not duration :
return False # ゴール時間がない場合は失格としない(競技中の可能性)
# duration( timedelta) に15分を加算
max_time = start_time + duration + timedelta ( minutes = 15 )
return goal_time > max_time
@api_view ( [ ' GET ' ] )
def get_ranking ( request , event_code , category_name ) :
""" 特定のイベントとクラスのランキングを取得する """
try :
# イベントの情報を取得
event = NewEvent2 . objects . get ( event_name = event_code )
# 有効なエントリーを取得
entries = Entry . objects . filter (
event = event ,
category__category_name = category_name ,
is_active = True
) . select_related ( ' team ' , ' category ' )
rankings = [ ]
disqualified = [ ] # 失格チームのリスト
for entry in entries :
# チェックインポイントを集計
checkins = GpsCheckin . objects . filter (
zekken_number = entry . zekken_number ,
event_code = event_code
) . aggregate (
total_points = Sum ( ' points ' )
)
# 最後のチェックイン時刻を取得
last_checkin = GpsCheckin . objects . filter (
zekken_number = entry . zekken_number ,
event_code = event_code
) . order_by ( ' -create_at ' ) . first ( )
last_checkin_time = last_checkin . create_at if last_checkin else None
# ゴール時間を取得 (最も早いゴール時間)
goal_record = GoalImages . objects . filter (
zekken_number = entry . zekken_number ,
event_code = event_code
) . order_by ( ' goaltime ' ) . first ( )
goal_time = goal_record . goaltime if goal_record else None
total_points = checkins [ ' total_points ' ] or 0
# イベントの終了予定時刻を計算
expected_end_time = event . start_datetime + entry . category . duration
# チーム状態の判定
team_status = get_team_status ( last_checkin_time , goal_time , expected_end_time )
# 失格判定
if is_disqualified ( event . start_datetime , goal_time , entry . category . duration ) :
disqualified . append ( {
' team_name ' : entry . team . team_name ,
' zekken_number ' : entry . zekken_number ,
' point ' : total_points ,
' late_point ' : 0 ,
' goal_time ' : goal_time ,
' reason ' : ' 制限時間超過 ' ,
' status ' : team_status
} )
continue
# 遅刻減点を計算
late_points = calculate_late_points ( goal_time , expected_end_time )
rankings . append ( {
' team_name ' : entry . team . team_name ,
' zekken_number ' : entry . zekken_number ,
' point ' : total_points ,
' late_point ' : abs ( late_points ) ,
' final_point ' : total_points + late_points ,
' goal_time ' : goal_time ,
' status ' : team_status ,
' last_checkin ' : last_checkin_time
} )
# ポイントの高い順(同点の場合はゴール時間が早い順)にソート
# 棄権チームを最後に
rankings . sort ( key = lambda x : (
- 1 if x [ ' status ' ] != ' 棄権 ' else 0 , # 棄権でないチームを優先
- x [ ' final_point ' ] , # 得点の高い順
x [ ' goal_time ' ] or datetime . max # ゴール時間の早い順
) )
# 有効なランキングと失格チームを結合
final_rankings = {
' rankings ' : rankings ,
' disqualified ' : disqualified
}
return Response ( final_rankings )
except NewEvent2 . DoesNotExist :
return Response (
{ " error " : " Specified event not found " } ,
status = status . HTTP_404_NOT_FOUND
)
except Exception as e :
logger . error ( f " Error in get_ranking: { str ( e ) } " , exc_info = True )
return Response (
{ " error " : str ( e ) } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
)
@api_view ( [ ' GET ' ] )
def all_ranking_top3 ( request , event_code ) :
""" 特定のイベントの全クラスのTOP3を取得する """
try :
2025-08-20 19:15:19 +09:00
event_code = unquote ( event_code ) # URLデコード
2024-11-12 07:19:18 +09:00
# イベントの情報を取得
event = NewEvent2 . objects . get ( event_name = event_code )
# イベントの有効なカテゴリーを取得
categories = NewCategory . objects . filter (
entry__event = event ,
entry__is_active = True
) . distinct ( )
rankings = { }
for category in categories :
# カテゴリーごとのエントリーを取得
entries = Entry . objects . filter (
event = event ,
category = category ,
is_active = True
) . select_related ( ' team ' )
category_rankings = [ ]
disqualified = [ ] # カテゴリーごとの失格チーム
for entry in entries :
# チェックインポイントを集計
checkins = GpsCheckin . objects . filter (
zekken_number = entry . zekken_number ,
event_code = event_code
) . aggregate (
total_points = Sum ( ' points ' )
)
# 最後のチェックイン時刻を取得
last_checkin = GpsCheckin . objects . filter (
zekken_number = entry . zekken_number ,
event_code = event_code
) . order_by ( ' -create_at ' ) . first ( )
last_checkin_time = last_checkin . create_at if last_checkin else None
# ゴール時間を取得
goal_record = GoalImages . objects . filter (
zekken_number = entry . zekken_number ,
event_code = event_code
) . order_by ( ' goaltime ' ) . first ( )
goal_time = goal_record . goaltime if goal_record else None
total_points = checkins [ ' total_points ' ] or 0
# イベントの終了予定時刻を計算
expected_end_time = event . start_datetime + category . duration
# チーム状態の判定
team_status = get_team_status ( last_checkin_time , goal_time , expected_end_time )
# 失格判定
if is_disqualified ( event . start_datetime , goal_time , entry . category . duration ) :
disqualified . append ( {
' team_name ' : entry . team . team_name ,
' zekken_number ' : entry . zekken_number ,
' point ' : total_points ,
' late_point ' : 0 ,
' goal_time ' : goal_time ,
' reason ' : ' 制限時間超過 ' ,
' status ' : team_status
} )
continue
# 遅刻減点を計算
late_points = calculate_late_points ( goal_time , expected_end_time )
category_rankings . append ( {
' team_name ' : entry . team . team_name ,
' zekken_number ' : entry . zekken_number ,
' point ' : total_points ,
' late_point ' : abs ( late_points ) ,
' final_point ' : total_points + late_points ,
' goal_time ' : goal_time ,
' status ' : team_status ,
' last_checkin ' : last_checkin_time
} )
# ポイントの高い順(同点の場合はゴール時間が早い順)にソート
# 棄権チームを最後に
category_rankings . sort ( key = lambda x : (
- 1 if x [ ' status ' ] != ' 棄権 ' else 0 , # 棄権でないチームを優先
- x [ ' final_point ' ] , # 得点の高い順
x [ ' goal_time ' ] or datetime . max # ゴール時間の早い順
) )
# TOP3のみを保持( 棄権を除く)
top_rankings = [ r for r in category_rankings if r [ ' status ' ] != ' 棄権 ' ] [ : 3 ]
rankings [ category . category_name ] = {
' rankings ' : top_rankings ,
' disqualified ' : disqualified
}
return Response ( rankings )
except NewEvent2 . DoesNotExist :
return Response (
{ " error " : " Specified event not found " } ,
status = status . HTTP_404_NOT_FOUND
)
except Exception as e :
logger . error ( f " Error in all_ranking_top3: { str ( e ) } " , exc_info = True )
return Response (
{ " error " : str ( e ) } ,
status = status . HTTP_500_INTERNAL_SERVER_ERROR
2024-12-19 03:58:48 +00:00
)
2025-08-24 19:44:36 +09:00
# ルートページ用のビュー
def index_view ( request ) :
""" ルートページをスーパーバイザー画面にリダイレクト """
from django . shortcuts import render
from django . http import HttpResponse
# supervisor/html/index.htmlを読み込んで返す
try :
import os
base_dir = os . path . dirname ( os . path . dirname ( os . path . abspath ( __file__ ) ) )
supervisor_path = os . path . join ( base_dir , ' supervisor ' , ' html ' , ' index.html ' )
with open ( supervisor_path , ' r ' , encoding = ' utf-8 ' ) as file :
content = file . read ( )
return HttpResponse ( content , content_type = ' text/html ' )
except Exception as e :
logger . error ( f " Error loading supervisor page: { str ( e ) } " )
return HttpResponse (
" <h1>System Error</h1><p>Failed to load supervisor interface</p> " ,
status = 500
)
2025-08-27 15:01:06 +09:00
# Import LocationCheckinView for evaluation_value-based interactions
from . location_checkin_view import LocationCheckinView
def location_checkin_test ( request ) :
""" ロケーションチェックインのテストページ """
from django . shortcuts import render
return render ( request , ' location_checkin_test.html ' )
2025-09-02 20:06:58 +09:00
@api_view ( [ ' GET ' ] )
@permission_classes ( [ IsAuthenticated ] )
def current_entry_info ( request ) :
"""
ユーザーの最新エントリー情報取得API
GET / api / user / current - entry - info /
"""
user = request . user
# ユーザーの最新エントリーを取得( IDの降順でソート)
latest_entry = Entry . objects . filter (
team__owner = user
) . select_related ( ' team ' , ' event ' , ' category ' , ' team__category ' ) . order_by ( ' -id ' ) . first ( )
if not latest_entry :
return Response ( {
' error ' : ' No active entry found for user ' ,
' detail ' : ' User has no current entries '
} , status = status . HTTP_404_NOT_FOUND )
# ユーザーの全エントリー数を取得
entries_count = Entry . objects . filter ( team__owner = user ) . count ( )
# レスポンスデータを構築
response_data = {
' user ' : {
' id ' : user . id ,
' email ' : user . email ,
' firstname ' : user . firstname ,
' lastname ' : user . lastname ,
' is_staff ' : user . is_staff ,
' event_code ' : latest_entry . event . event_name # 最新エントリーのイベント名
} ,
' current_entry ' : {
' id ' : latest_entry . id ,
' team ' : {
' id ' : latest_entry . team . id ,
' team_name ' : latest_entry . team . team_name ,
' category ' : {
' id ' : latest_entry . category . id ,
' category_name ' : latest_entry . category . category_name
}
} ,
' event ' : {
' id ' : latest_entry . event . id ,
' event_name ' : latest_entry . event . event_name ,
' start_datetime ' : latest_entry . event . start_datetime ,
' end_datetime ' : latest_entry . event . end_datetime
} ,
' date ' : latest_entry . date . strftime ( ' % Y- % m- %d ' ) if latest_entry . date else None ,
' has_participated ' : latest_entry . hasParticipated ,
' has_goaled ' : latest_entry . hasGoaled
} ,
' entries_count ' : entries_count ,
' latest_entry_date ' : latest_entry . date # created_atがないのでdateフィールドを使用
}
return Response ( response_data )