Files
rogaining_srv/rog/serializers.py

1172 lines
47 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from django.contrib.auth.hashers import make_password, check_password
from django.contrib.auth import get_user_model
User = get_user_model()
import uuid
from django.db import IntegrityError
from django.conf import settings
from django.urls import reverse
from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError
from django.db import transaction
from rest_framework import serializers
from rest_framework_gis.serializers import GeoFeatureModelSerializer
from sqlalchemy.sql.functions import mode
from .models import Location2025, Location_line, Location_polygon, JpnAdminMainPerf, Useractions, GifuAreas, RogUser, UserTracks, GoalImages, CheckinImages,CustomUser,NewEvent,NewEvent2, Team, NewCategory, Category, Entry, Member, TempUser,EntryMember, AppVersion, UploadedImage
from drf_extra_fields.fields import Base64ImageField
#from django.contrib.auth.models import User
from .models import CustomUser
from django.contrib.auth import authenticate
from .models import TestModel
import logging
from django.shortcuts import get_object_or_404
from django.utils import timezone
from datetime import datetime, date
logger = logging.getLogger(__name__)
class LocationCatSerializer(serializers.ModelSerializer):
class Meta:
model=Location2025
fields=['category',]
class LocationSerializer(serializers.ModelSerializer):
# evaluation_valueに基づくインタラクション情報を追加
interaction_type = serializers.SerializerMethodField()
requires_photo = serializers.SerializerMethodField()
requires_qr_code = serializers.SerializerMethodField()
interaction_instructions = serializers.SerializerMethodField()
# 追加フィールドのカスタムシリアライズ
has_photos = serializers.SerializerMethodField()
has_videos = serializers.SerializerMethodField()
photos_list = serializers.SerializerMethodField()
videos_list = serializers.SerializerMethodField()
tags_list = serializers.SerializerMethodField()
# 位置情報の緯度経度
latitude_float = serializers.SerializerMethodField()
longitude_float = serializers.SerializerMethodField()
class Meta:
model=Location2025
fields=[
# 基本フィールド
'id', 'cp_number', 'event', 'cp_name', 'sub_loc_id', 'subcategory',
'latitude', 'longitude', 'location', 'address',
'cp_point', 'photo_point', 'buy_point',
'checkin_radius', 'auto_checkin',
'shop_closed', 'shop_shutdown', 'opening_hours',
'phone', 'website', 'description', 'remark',
# 追加フィールド
'photos', 'videos', 'tags', 'evaluation_value', 'hidden_location',
# 管理フィールド
'is_active', 'sort_order', 'csv_source_file', 'csv_upload_date',
'created_at', 'updated_at', 'created_by', 'updated_by',
# カスタムフィールド
'interaction_type', 'requires_photo', 'requires_qr_code', 'interaction_instructions',
'has_photos', 'has_videos', 'photos_list', 'videos_list', 'tags_list',
'latitude_float', 'longitude_float'
]
def get_latitude_float(self, obj):
"""位置情報から緯度を取得"""
if obj.location:
return obj.location.y
return obj.latitude
def get_longitude_float(self, obj):
"""位置情報から経度を取得"""
if obj.location:
return obj.location.x
return obj.longitude
def get_has_photos(self, obj):
"""写真データの有無を返す"""
return bool(obj.photos and obj.photos.strip())
def get_has_videos(self, obj):
"""動画データの有無を返す"""
return bool(obj.videos and obj.videos.strip())
def get_photos_list(self, obj):
"""写真ファイル名をリストで返す"""
if not obj.photos or not obj.photos.strip():
return []
# カンマ区切りで分割してリストとして返す
return [photo.strip() for photo in obj.photos.split(',') if photo.strip()]
def get_videos_list(self, obj):
"""動画ファイル名をリストで返す"""
if not obj.videos or not obj.videos.strip():
return []
# カンマ区切りで分割してリストとして返す
return [video.strip() for video in obj.videos.split(',') if video.strip()]
def get_tags_list(self, obj):
"""タグをリストで返す"""
if not obj.tags or not obj.tags.strip():
return []
# カンマ区切りで分割してリストとして返す
return [tag.strip() for tag in obj.tags.split(',') if tag.strip()]
def get_interaction_type(self, obj):
"""evaluation_valueに基づくインタラクションタイプを返す"""
try:
from .location_interaction import get_interaction_type
return get_interaction_type(obj)['type']
except ImportError:
return obj.evaluation_value or "0"
def get_requires_photo(self, obj):
"""写真撮影が必要かどうかを返す"""
try:
from .location_interaction import get_interaction_type
return get_interaction_type(obj)['requires_photo']
except ImportError:
return obj.evaluation_value == "1"
def get_requires_qr_code(self, obj):
"""QRコードスキャンが必要かどうかを返す"""
try:
from .location_interaction import should_use_qr_code
return should_use_qr_code(obj)
except ImportError:
return obj.evaluation_value == "2" or getattr(obj, 'use_qr_code', False)
def get_interaction_instructions(self, obj):
"""インタラクション手順を返す"""
try:
from .location_interaction import get_interaction_type
return get_interaction_type(obj)['instructions']
except ImportError:
evaluation_value = obj.evaluation_value or "0"
if evaluation_value == "1":
return "商品の写真を撮影してください。買い物をすることでポイントを獲得できます"
elif evaluation_value == "2":
return "QRコードをスキャンしてクイズに答えてください"
else:
return "この場所でチェックインしてポイントを獲得してください"
class Location_lineSerializer(GeoFeatureModelSerializer):
class Meta:
model=Location_line
geo_field='geom'
fields="__all__"
class Location_polygonSerializer(GeoFeatureModelSerializer):
class Meta:
model=Location_polygon
geo_field='geom'
fields="__all__"
class JPN_main_perfSerializer(serializers.ModelSerializer):
class Meta:
model=JpnAdminMainPerf
fields=['id', 'adm0_en', 'adm0_ja', 'adm0_pcode', 'adm1_en', 'adm1_ja', 'adm1_pcode']
# class JPN_sub_perSerializer(serializers.ModelSerializer):
# class Meta:
# model=JpnSubPerf
# fields=['id', 'adm0_en', 'adm0_ja', 'adm0_pcode', 'adm1_en', 'adm1_ja', 'adm1_pcode', 'adm2_ja', 'adm2_en', 'adm2_pcode']
# class JPN_perfSerializer(serializers.ModelSerializer):
# class Meta:
# model=JpnAdminPerf
# fields=['id','et_id', 'et_right', 'et_left', 'adm2_l', 'adm1_l', 'adm0_l', 'adm0_r', 'adm1_r', 'adm2_r', 'admlevel']
class GifuAreaSerializer(serializers.ModelSerializer):
class Meta:
model= GifuAreas
fields=['id', 'adm0_ja', 'adm0_pcode', 'adm1_en', 'adm1_ja', 'adm1_pcode', 'adm2_ja', 'adm2_en', 'adm2_pcode', 'area_nm']
class UserRegistrationSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, required=True, validators=[validate_password])
password2 = serializers.CharField(write_only=True, required=True, validators=[validate_password])
class Meta:
model = CustomUser
fields = ('email', 'password', 'password2', 'firstname', 'lastname', 'date_of_birth', 'female')
extra_kwargs = {
'email': {'required': True},
'firstname': {'required': True},
'lastname': {'required': True},
'date_of_birth': {'required': True},
}
def validate(self, attrs):
if attrs['password'] != attrs['password2']:
raise serializers.ValidationError({"password": "Password fields didn't match."})
try:
validate_password(attrs['password'])
except ValidationError as e:
raise serializers.ValidationError({"password": list(e.messages)})
return attrs
def validate_email(self, value):
if CustomUser.objects.filter(email=value).exists() or TempUser.objects.filter(email=value).exists():
raise serializers.ValidationError("この電子メールアドレスは既に使用されています。")
return value
def create(self, validated_data):
raw_password = validated_data.get('password')
# デバッグコード
hashed_password = make_password(raw_password)
print(f"Hashed password during registration: {hashed_password}")
is_valid = check_password(raw_password, hashed_password)
print(f"Password is valid during registration: {is_valid}")
validated_data['password'] = hashed_password
return super(UserRegistrationSerializer, self).create(validated_data)
#validated_data['password'] = make_password(validated_data.get('password'))
#return super(UserRegistrationSerializer, self).create(validated_data)
# try:
# with transaction.atomic():
# password = validated_data['password'].encode('utf-8').decode('utf-8')
#
# user = CustomUser.objects.create_user(
# email=validated_data['email'],
# password=password, # validated_data['password'],
# firstname=validated_data['firstname'],
# lastname=validated_data['lastname'],
# date_of_birth=validated_data['date_of_birth'],
# female=validated_data.get('female', False),
# group='' # この値は必要に応じて変更してください
# )
# logger.debug(f"Creating user with data: {validated_data}")
# user.set_password(validated_data['password'])
# user.save()
#
# return user
# except ValidationError as e:
# raise serializers.ValidationError({"password": list(e.messages)})
#class CreateUserSerializer(serializers.ModelSerializer):
# class Meta:
# model = CustomUser
# fields = ('email', 'password')
# extra_kwargs = {'password': {'write_only': True}}
#
# def create(self, validated_data):
# user = CustomUser.objects.create_user(validated_data['email'],validated_data['password'], '大垣-初心者','','')
# return user
class TempUserRegistrationSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
class Meta:
model = TempUser
fields = ('email', 'password', 'firstname', 'lastname', 'date_of_birth', 'female')
def create(self, validated_data):
# パスワードのハッシュ化はviewで行うので、ここではそのまま保存
return TempUser.objects.create(**validated_data)
#validated_data['verification_code'] = str(uuid.uuid4())
#raw_password = validated_data.get('password')
#hashed_password = make_password(raw_password)
#validated_data['password'] = hashed_password
#return TempUser.objects.create(**validated_data)
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ('id','email', 'is_rogaining' ,'group', 'zekken_number', 'event_code', 'team_name')
class GolaImageSerializer(serializers.ModelSerializer):
goalimage = Base64ImageField(max_length=None, use_url=True)
class Meta:
model = GoalImages
fields="__all__"
def get_goalimage_url_old(self, car):
request = self.context.get('request')
photo_url = GoalImages.goalimage.url
return request.build_absolute_uri(photo_url)
def get_goalimage_url(self, obj):
request = self.context.get('request')
if request is None:
logger.warning("Request not found in serializer context")
return None
try:
photo_url = obj.goalimage.url
absolute_url = request.build_absolute_uri(photo_url)
logger.info(f"Generated URL for goalimage: {absolute_url}")
return absolute_url
except AttributeError as e:
logger.error(f"Error generating URL for goalimage: {str(e)}")
return None
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['goalimage_url'] = self.get_goalimage_url(instance)
logger.debug(f"Serialized data: {representation}")
return representation
class CheckinImageSerializer(serializers.ModelSerializer):
checkinimage = Base64ImageField(max_length=None, use_url=True)
class Meta:
model = CheckinImages
fields="__all__"
def get_checkinimage_url(self, car):
request = self.context.get('request')
photo_url = CheckinImages.checkinimage.url
return request.build_absolute_uri(photo_url)
class RogUserSerializer(serializers.ModelSerializer):
class Meta:
model = RogUser
fields = ('id','user', 'paid',)
class LoginUserSerializer(serializers.Serializer):
#email = serializers.CharField()
email = serializers.EmailField()
password = serializers.CharField()
def validate(self, data):
email = data.get('email')
password = data.get('password')
if email and password:
user = authenticate(username=email, password=password)
if user:
if user.is_active:
return user
raise serializers.ValidationError("User account is disabled.")
else:
# Check if the user exists
try:
user_obj = User.objects.get(email=email)
raise serializers.ValidationError("Incorrect password.")
except User.DoesNotExist:
raise serializers.ValidationError("User with this email does not exist.")
else:
raise serializers.ValidationError("Must include 'email' and 'password'.")
class UseractionsSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
#location = LocationSerializer(read_only=True)
location = serializers.RelatedField(source='Location', read_only=True)
#location = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Useractions
fields = ('id', 'user', 'location', 'wanttogo', 'like', 'checkin', 'order',)
class UserDestinationSerializer(serializers.ModelSerializer):
#user = UserSerializer(read_only=True)
location = LocationSerializer(read_only=True)
#location = serializers.RelatedField(source='Location', read_only=True)
#location = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Useractions
fields = ('id', 'user', 'location', 'wanttogo', 'like', 'checkin')
class LocationEventNameSerializer(serializers.ModelSerializer):
class Meta:
model = Location2025
fields = ('id', 'event_name',)
class UserTracksSerializer(GeoFeatureModelSerializer):
user_id = serializers.IntegerField()
class Meta:
model=UserTracks
geo_field = 'geom'
fields = ["user_id",]
def create(self, validated_data):
user_id = validated_data.pop("user_id", None)
user = CustomUser.objects.get(id=user_id)
return UserTracks.objects.create(user=user, **validated_data)
class TestSerialiser(serializers.ModelSerializer):
class Meta:
model = TestModel
fields = ('id', 'testbane', 'wanttogo', 'like', 'checkin')
class ChangePasswordSerializer(serializers.Serializer):
model = CustomUser
"""
Serializer for password change endpoint.
"""
old_password = serializers.CharField(required=True)
new_password = serializers.CharField(required=True)
class RegistrationSerializer(serializers.ModelSerializer):
password2 = serializers.CharField(style={"input_type": "password"}, write_only=True)
class Meta:
model = CustomUser
fields = ['email', 'is_rogaining', 'zekken_number', 'event_code', 'team_name', 'group', 'password', 'password2']
extra_kwargs = {
'password': {'write_only': True}
}
def save(self):
user = CustomUser(email=self.validated_data['email'], is_rogaining=self.validated_data['is_rogaining'], zekken_number=self.validated_data['zekken_number'], event_code=self.validated_data['event_code'], team_name=self.validated_data['team_name'], group=self.validated_data['group'])
password = self.validated_data['password']
password2 = self.validated_data['password2']
if password != password2:
raise serializers.ValidationError({'password': 'Passwords must match.'})
user.set_password(password)
user.save()
return user
class NewCategorySerializer(serializers.ModelSerializer):
class Meta:
model = NewCategory
fields = ['id','category_name', 'category_number', 'duration', 'num_of_member', 'family', 'female']
#fields = ['id','category_name', 'category_number']
class NewEvent2Serializer(serializers.ModelSerializer):
# API変更要求書対応: deadline_datetime フィールド追加
deadline_datetime = serializers.DateTimeField(source='deadlineDateTime', read_only=True)
# Supervisor web app compatibility
code = serializers.CharField(source='event_name', read_only=True)
name = serializers.CharField(source='event_name', read_only=True)
class Meta:
model = NewEvent2
fields = [
'id', 'event_name', 'start_datetime', 'end_datetime',
'deadlineDateTime', 'deadline_datetime', 'status', 'public',
'hour_3', 'hour_5', 'class_general', 'class_family',
'class_solo_male', 'class_solo_female',
'code', 'name' # Supervisor compatibility
]
def to_representation(self, instance):
"""レスポンス形式を調整"""
data = super().to_representation(instance)
# publicフィールドからstatusへの移行サポート
if not data.get('status') and data.get('public'):
data['status'] = 'public'
elif not data.get('status'):
data['status'] = 'draft'
return data
class NewEventSerializer(serializers.ModelSerializer):
class Meta:
model = NewEvent
fields = ['event_name', 'start_datetime', 'end_datetime']
class TeamSerializer(serializers.ModelSerializer):
category = serializers.PrimaryKeyRelatedField(queryset=NewCategory.objects.all())
#category = serializers.IntegerField()
#category = NewCategorySerializer(read_only=True)
#category_id = serializers.PrimaryKeyRelatedField(
# queryset=NewCategory.objects.all(),
# source='category',
# write_only=True
#)
owner = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Team
fields = ['id','team_name', 'category', 'owner']
read_only_fields = ['id', 'owner']
def to_representation(self, instance):
ret = super().to_representation(instance)
if instance.category:
ret['category'] = {
'id': instance.category.id,
'category_name': instance.category.category_name,
'category_number': instance.category.category_number,
'duration': instance.category.duration,
'num_of_member':instance.category.num_of_member,
'family':instance.category.family,
'female':instance.category.female
}
else:
ret['category'] = None
ret['owner'] = CustomUserSerializer(instance.owner).data
return ret
def validate_category(self, value):
if not isinstance(value, NewCategory):
raise serializers.ValidationError("Invalid category")
return value
#if not NewCategory.objects.filter(id=value).exists():
# raise serializers.ValidationError("Invalid category ID")
#return value
def create(self, validated_data):
return Team.objects.create(**validated_data)
#category_id = validated_data.pop('category')
#category = get_object_or_404(NewCategory, id=category_id)
#team = Team.objects.create(category=category, **validated_data)
#team.category = category
#return team
#category = validated_data.pop('category')
#team = Team.objects.create(category=category, **validated_data)
#return team
#logger.debug(f"Creating team with data: {validated_data}")
#validated_data['owner'] = self.context['request'].user
#return super().create(validated_data)
def update(self, instance, validated_data):
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
return instance
#if 'category' in validated_data:
# category_id = validated_data.pop('category')
# category = get_object_or_404(NewCategory, id=category_id)
# instance.category = category
#return super().update(instance, validated_data)
#if 'category' in validated_data:
# instance.category = validated_data.pop('category')
#return super().update(instance, validated_data)
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['category_name', 'category_number', 'duration', 'num_of_member', 'family', 'female']
class EntrySerializer(serializers.ModelSerializer):
team = serializers.PrimaryKeyRelatedField(queryset=Team.objects.all())
event = serializers.PrimaryKeyRelatedField(queryset=NewEvent2.objects.all())
category = serializers.PrimaryKeyRelatedField(queryset=NewCategory.objects.all())
owner = serializers.PrimaryKeyRelatedField(read_only=True)
#date = serializers.DateTimeField(input_formats=['%Y-%m-%d'])
date = serializers.DateField(required=False, allow_null=True) # DateTimeFieldではなくDateFieldを使用
zekken_number = serializers.IntegerField()
#date = serializers.DateTimeField(default_timezone=timezone.get_current_timezone())
class Meta:
model = Entry
fields = [
'id', 'team', 'event', 'category', 'date', 'zekken_number', 'owner',
'is_active', 'hasParticipated', 'hasGoaled',
# API変更要求書対応: 新フィールド追加
'staff_privileges', 'can_access_private_events', 'team_validation_status'
]
read_only_fields = ['id', 'owner']
def validate_date(self, value):
if isinstance(value, str):
try:
value = datetime.strptime(value, "%Y-%m-%d")
except ValueError:
raise serializers.ValidationError("Invalid date format. Use YYYY-MM-DD.")
if isinstance(value, date):
value = datetime.combine(value, datetime.min.time())
if timezone.is_naive(value):
return timezone.make_aware(value, timezone.get_current_timezone())
return value
#if isinstance(value, date):
# # dateオブジェクトをdatetimeオブジェクトに変換
# value = datetime.combine(value, datetime.min.time())
#if timezone.is_naive(value):
# return timezone.make_aware(value, timezone.get_current_timezone())
#return value
def validate_team(self, value):
if not value.members.exists():
raise serializers.ValidationError("チームにメンバーが登録されていません。")
return value
def validate_date(self, value):
if isinstance(value, datetime):
return value.date()
return value
def validate(self, data):
team = data.get('team')
event = data.get('event')
category = data.get('category')
entry_date = data.get('date')
if isinstance(entry_date, datetime):
entry_date = entry_date.date()
elif isinstance(entry_date, str):
entry_date = datetime.strptime(entry_date, "%Y-%m-%d").date()
logger.debug("test-0")
logger.debug(f"==== start:{event.start_datetime.date()} <= entry_date : {entry_date} <= end:{event.end_datetime.date()} ?? ====")
if entry_date < event.start_datetime.date() or entry_date > event.end_datetime.date():
raise serializers.ValidationError(f"日付は{event.start_datetime.date()}から{event.end_datetime.date()}の間である必要があります。")
logger.debug("test-1")
try:
logger.debug(f"Parsed data: team={team}, event={event}, category={category}, ")
owner = self.context['request'].user
zekken_number = data.get('zekken_number')
logger.debug(f"entry_date={entry_date}, owner={owner}, zekken_number={zekken_number}")
except Exception:
raise serializers.ValidationError(f"何らかのエラーが発生しました")
# Check if team, event, and category exist
if not Team.objects.filter(id=team.id).exists():
raise serializers.ValidationError("指定されたチームは存在しません。")
if not NewEvent2.objects.filter(id=event.id).exists():
raise serializers.ValidationError("指定されたイベントは存在しません。")
if not NewCategory.objects.filter(id=category.id).exists():
raise serializers.ValidationError("指定されたカテゴリーは存在しません。")
# Check for unique constraint
if Entry.objects.filter(team=team, event=event, date__date=entry_date, owner=owner).exists():
raise serializers.ValidationError("既に登録済みです。")
# Validate zekken_number
if zekken_number is not None:
if zekken_number <= 0:
raise serializers.ValidationError("ゼッケン番号は正の整数である必要があります。")
# if Entry.objects.filter(event=event, zekken_number=zekken_number).exists():
# raise serializers.ValidationError("このゼッケン番号は既に使用されています。")
return data
def to_internal_value(self, data):
# dateフィールドが文字列で来た場合の処理
if 'date' in data and isinstance(data['date'], str):
try:
# 文字列をdatetimeオブジェクトに変換
data['date'] = datetime.strptime(data['date'], "%Y-%m-%d")
except ValueError:
raise serializers.ValidationError({"date": "無効な日付形式です。YYYY-MM-DD形式を使用してください。"})
return super().to_internal_value(data)
def to_representation(self, instance):
ret = super().to_representation(instance)
ret['team'] = TeamSerializer(instance.team).data
ret['event'] = NewEvent2Serializer(instance.event).data
ret['category'] = NewCategorySerializer(instance.category).data
ret['owner'] = CustomUserSerializer(instance.owner).data
if isinstance(ret['date'], datetime):
ret['date'] = ret['date'].date().isoformat()
elif isinstance(ret['date'], date):
ret['date'] = ret['date'].isoformat()
return ret
#if isinstance(ret['date'], datetime):
# ret['date'] = ret['date'].date().isoformat()
#return ret
#def to_representation(self, instance):
# ret = super().to_representation(instance)
# ret['team'] = instance.team.team_name
# ret['event'] = instance.event.event_name
# ret['category'] = instance.category.category_name
# ret['owner'] = instance.owner.email
# return ret
class CustomUserSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ['id','email', 'firstname', 'lastname', 'date_of_birth', 'female']
read_only_fields = ['id','email']
class TeamDetailSerializer(serializers.ModelSerializer):
category = NewCategorySerializer(read_only=True)
class Meta:
model = Team
fields = ['id', 'zekken_number', 'team_name', 'category']
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ['id','email', 'firstname', 'lastname', 'date_of_birth', 'female', 'is_rogaining', 'zekken_number', 'event_code', 'team_name', 'group']
read_only_fields = ('id', 'email')
class UserUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ['firstname', 'lastname', 'date_of_birth', 'female']
extra_kwargs = {'email': {'read_only': True}}
def update(self, instance, validated_data):
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
return instance
class MemberCreationSerializer(serializers.Serializer):
#email = serializers.EmailField()
email = serializers.EmailField(allow_blank=True, required=False)
firstname = serializers.CharField(required=False, allow_blank=True)
lastname = serializers.CharField(required=False, allow_blank=True)
date_of_birth = serializers.DateField(required=False)
female = serializers.BooleanField(required=False)
class MemberWithUserSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
class Meta:
model = Member
fields = ['user', 'team']
class MemberSerializer(serializers.ModelSerializer):
email = serializers.EmailField(write_only=True)
firstname = serializers.CharField(required=False, allow_blank=True, allow_null=True)
lastname = serializers.CharField(required=False, allow_blank=True, allow_null=True)
date_of_birth = serializers.DateField(required=False, allow_null=True)
female = serializers.BooleanField(required=False)
class Meta:
model = Member
fields = ['id', 'email', 'firstname', 'lastname', 'date_of_birth', 'female']
def validate_firstname(self, value):
return value or None
def validate_lastname(self, value):
return value or None
def create(self, validated_data):
email = validated_data.pop('email')
team = self.context['team']
# 既存のユーザーを探すか、新しいユーザーを作成
user, created = CustomUser.objects.get_or_create(email=email)
# ユーザーが新しく作成された場合のみ、追加情報を更新
if created:
user.firstname = validated_data.get('firstname', '')
user.lastname = validated_data.get('lastname', '')
user.date_of_birth = validated_data.get('date_of_birth')
user.female = validated_data.get('female', False)
user.save()
# メンバーを作成
member = Member.objects.create(
user=user,
team=team,
firstname=validated_data.get('firstname'),
lastname=validated_data.get('lastname'),
date_of_birth=validated_data.get('date_of_birth'),
female=validated_data.get('female', False)
)
return member
# メンバーを作成して返す
#class MemberCreationSerializerreturn Member.objects.create(user=user, team=team)
def update(self, instance, validated_data):
user_data = validated_data.pop('user', {})
user = instance.user
for attr, value in user_data.items():
setattr(user, attr, value)
user.save()
return super().update(instance, validated_data)
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['email'] = instance.user.email
representation['firstname'] = instance.user.firstname
representation['lastname'] = instance.user.lastname
representation['date_of_birth'] = instance.user.date_of_birth
representation['female'] = instance.user.female
return representation
class MemberSerializerOld(serializers.ModelSerializer):
user = CustomUserSerializer(read_only=True)
firstname = serializers.CharField(required=False, allow_blank=True, allow_null=True)
lastname = serializers.CharField(required=False, allow_blank=True, allow_null=True)
date_of_birth = serializers.DateField(required=False, allow_null=True)
female = serializers.BooleanField(required=False)
#team = TeamDetailSerializer(read_only=True)
#email = serializers.EmailField(write_only=True, required=False)
#firstname = serializers.CharField(write_only=True, required=False)
#lastname = serializers.CharField(write_only=True, required=False)
#date_of_birth = serializers.DateField(write_only=True, required=False)
#female = serializers.BooleanField(write_only=True, required=False)
class Meta:
model = Member
fields = ['id','email','firstname','lastname','date_of_birth','female']
#read_only_fields = ['id', 'team']
'''
def create(self, validated_data):
team = validated_data['team']
email = validated_data.get('email')
if email.startswith('dummy_'):
user, _ = CustomUser.objects.get_or_create(
email=email,
defaults={**user_data, 'is_active': True}
)
else:
user, _ = CustomUser.objects.get_or_create(
email=email,
defaults={**user_data, 'is_active': False}
)
member = Member.objects.create(user=user, **validated_data)
return member
'''
def create(self, validated_data):
email = validated_data.pop('email')
team = self.context['team']
# 既存のユーザーを探すか、新しいユーザーを作成
user, created = CustomUser.objects.get_or_create(email=email)
# メンバーを作成
member = Member.objects.create(
user=user,
team=team,
firstname=validated_data.get('firstname', ''),
lastname=validated_data.get('lastname', ''),
date_of_birth=validated_data.get('date_of_birth'),
female=validated_data.get('female', False)
)
return member
def update(self, instance, validated_data):
user_data = validated_data.pop('user', {})
user = instance.user
for attr, value in user_data.items():
setattr(user, attr, value)
user.save()
return super().update(instance, validated_data)
#if user.email.startswith('dummy_'): # dummy_ で始まるメールアドレスの場合のみ更新
# for attr, value in user_data.items():
# setattr(user, attr, value)
# user.save()
#else:
# raise serializers.ValidationError("このユーザーの情報は更新できません。")
#return super().update(instance, validated_data)
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['email'] = instance.user.email
return representation
'''
def to_representation(self, instance):
representation = super().to_representation(instance)
user_data = representation['user']
return {
'id': representation['id'],
'email': user_data['email'],
'firstname': user_data['firstname'],
'lastname': user_data['lastname'],
'date_of_birth': user_data['date_of_birth'],
'female': user_data['female'],
'team': representation['team']
}
'''
class EntryMemberSerializer(serializers.ModelSerializer):
class Meta:
model = EntryMember
fields = ['id', 'entry', 'member', 'is_temporary']
class TempUserSerializer(serializers.ModelSerializer):
class Meta:
model = TempUser
#fields = ['id','email', 'password', 'is_rogaining', 'zekken_number', 'event_code', 'team_name', 'group', 'firstname', 'lastname', 'date_of_birth', 'female', 'verification_code', 'created_at', 'expires_at']
fields = ['email', 'password', 'firstname', 'lastname', 'date_of_birth', 'female', 'verification_code']
class EntryCreationSerializer(serializers.Serializer):
owner_email = serializers.EmailField()
event_name = serializers.CharField()
category_name = serializers.CharField()
team_name = serializers.CharField()
zekken_number = serializers.CharField()
date = serializers.DateField()
members = serializers.ListField(child=serializers.DictField())
def create(self, validated_data):
owner = CustomUser.objects.get(email=validated_data['owner_email'])
event = NewEvent2.objects.get(event_name=validated_data['event_name'])
category = NewCategory.objects.get(category_name=validated_data['category_name'])
# Create or get team
team, _ = Team.objects.get_or_create(
zekken_number=validated_data['zekken_number'],
category=category,
defaults={'team_name': validated_data['team_name'], 'owner': owner}
)
# Create or update entry
entry, _ = Entry.objects.update_or_create(
owner=owner,
team=team,
event=event,
date=validated_data['date'],
defaults={'category': category}
)
# Process members
for member_data in validated_data['members']:
user, created = CustomUser.objects.get_or_create(
email=member_data.get('email'),
defaults={
'firstname': member_data['firstname'],
'lastname': member_data['lastname'],
'date_of_birth': member_data['date_of_birth']
}
)
if created:
TempUser.objects.create(
email=user.email,
firstname=user.firstname,
lastname=user.lastname,
date_of_birth=user.date_of_birth
)
# Send invitation email here
member, _ = NewMember.objects.get_or_create(
user=user,
team=team,
defaults={'is_temporary': created}
)
EntryMember.objects.get_or_create(entry=entry, member=member)
return entry
class PasswordResetRequestSerializer(serializers.Serializer):
email = serializers.EmailField()
class PasswordResetConfirmSerializer(serializers.Serializer):
new_password = serializers.CharField(write_only=True)
confirm_password = serializers.CharField(write_only=True)
def validate(self, data):
if data['new_password'] != data['confirm_password']:
raise serializers.ValidationError("Passwords do not match")
validate_password(data['new_password'])
return data
class UserLastGoalTimeSerializer(serializers.Serializer):
user_email = serializers.EmailField()
last_goal_time = serializers.DateTimeField()
class LoginUserSerializer_old(serializers.Serializer):
identifier = serializers.CharField(required=True) # メールアドレスまたはゼッケン番号
password = serializers.CharField(required=True)
def validate(self, data):
identifier = data.get('identifier')
password = data.get('password')
if not identifier or not password:
raise serializers.ValidationError('認証情報を入力してください。')
# ゼッケン番号かメールアドレスかを判定
if '@' in identifier:
# メールアドレスの場合
user = authenticate(username=identifier, password=password)
else:
# ゼッケン番号の場合
try:
# ゼッケン番号からユーザーを検索
user = CustomUser.objects.filter(zekken_number=identifier).first()
if user:
# パスワード認証
if not user.check_password(password):
user = None
except ValueError:
user = None
if user and user.is_active:
return user
elif user and not user.is_active:
raise serializers.ValidationError('アカウントが有効化されていません。')
else:
raise serializers.ValidationError('認証情報が正しくありません。')
class AppVersionSerializer(serializers.ModelSerializer):
"""アプリバージョン管理シリアライザー"""
class Meta:
model = AppVersion
fields = [
'id', 'version', 'platform', 'build_number',
'is_latest', 'is_required', 'update_message',
'download_url', 'release_date', 'created_at'
]
read_only_fields = ['id', 'created_at']
class AppVersionCheckSerializer(serializers.Serializer):
"""アプリバージョンチェック用シリアライザー"""
current_version = serializers.CharField(max_length=20, help_text="現在のアプリバージョン")
platform = serializers.ChoiceField(
choices=[('android', 'Android'), ('ios', 'iOS')],
help_text="プラットフォーム"
)
build_number = serializers.CharField(max_length=20, required=False, help_text="ビルド番号")
class AppVersionResponseSerializer(serializers.Serializer):
"""アプリバージョンチェックレスポンス用シリアライザー"""
latest_version = serializers.CharField(help_text="最新バージョン")
update_required = serializers.BooleanField(help_text="強制更新が必要かどうか")
update_available = serializers.BooleanField(help_text="更新が利用可能かどうか")
update_message = serializers.CharField(help_text="更新メッセージ")
download_url = serializers.URLField(help_text="ダウンロードURL")
release_date = serializers.DateTimeField(help_text="リリース日時")
class UploadedImageSerializer(serializers.ModelSerializer):
"""画像アップロード情報シリアライザー"""
file_size_mb = serializers.ReadOnlyField()
class Meta:
model = UploadedImage
fields = [
'id', 'original_filename', 'server_filename', 'file_url',
'file_size', 'file_size_mb', 'mime_type', 'event_code',
'team_name', 'cp_number', 'upload_source', 'device_platform',
'capture_timestamp', 'upload_timestamp', 'device_info',
'processing_status', 'thumbnail_url', 'created_at', 'updated_at'
]
read_only_fields = ['id', 'server_filename', 'file_url', 'upload_timestamp', 'created_at', 'updated_at']
class MultiImageUploadSerializer(serializers.Serializer):
"""マルチ画像アップロード用シリアライザー"""
event_code = serializers.CharField(max_length=50)
team_name = serializers.CharField(max_length=255)
cp_number = serializers.IntegerField()
images = serializers.ListField(
child=serializers.DictField(),
max_length=10, # 最大10ファイル
help_text="アップロードする画像情報のリスト"
)
upload_source = serializers.ChoiceField(
choices=['direct', 'sharing_intent', 'bulk_upload'],
default='direct'
)
device_platform = serializers.ChoiceField(
choices=['ios', 'android', 'web'],
required=False
)
def validate_images(self, value):
"""画像データの検証"""
if not value:
raise serializers.ValidationError("画像が指定されていません")
total_size = 0
for image_data in value:
# 必須フィールドチェック
required_fields = ['file_data', 'filename', 'mime_type', 'file_size']
for field in required_fields:
if field not in image_data:
raise serializers.ValidationError(f"画像データに{field}が含まれていません")
# ファイルサイズチェック
file_size = image_data.get('file_size', 0)
if file_size > 10485760: # 10MB
raise serializers.ValidationError(f"ファイル{image_data['filename']}のサイズが10MBを超えています")
total_size += file_size
# MIMEタイプチェック
allowed_types = ['image/jpeg', 'image/png', 'image/heic', 'image/webp']
if image_data.get('mime_type') not in allowed_types:
raise serializers.ValidationError(f"サポートされていないファイル形式: {image_data.get('mime_type')}")
# 合計サイズチェック50MB
if total_size > 52428800:
raise serializers.ValidationError("合計ファイルサイズが50MBを超えています")
return value
class MultiImageUploadResponseSerializer(serializers.Serializer):
"""マルチ画像アップロードレスポンス用シリアライザー"""
status = serializers.CharField()
uploaded_count = serializers.IntegerField()
failed_count = serializers.IntegerField()
uploaded_files = serializers.ListField(
child=serializers.DictField()
)
total_upload_size = serializers.IntegerField()
processing_time_ms = serializers.IntegerField()