2024-08-02 07:01:32 +00:00
|
|
|
|
from django.contrib.auth.hashers import make_password, check_password
|
|
|
|
|
|
|
|
|
|
|
|
from django.contrib.auth import get_user_model
|
|
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
|
2024-07-25 00:57:48 +00:00
|
|
|
|
import uuid
|
|
|
|
|
|
from django.db import IntegrityError
|
|
|
|
|
|
from django.conf import settings
|
|
|
|
|
|
from django.urls import reverse
|
2024-07-26 12:34:54 +00:00
|
|
|
|
from django.contrib.auth.password_validation import validate_password
|
2024-08-02 07:01:32 +00:00
|
|
|
|
from django.core.exceptions import ValidationError
|
2024-07-25 00:57:48 +00:00
|
|
|
|
|
|
|
|
|
|
from django.db import transaction
|
2022-03-16 17:46:22 +05:30
|
|
|
|
from rest_framework import serializers
|
2022-03-14 10:50:11 +05:30
|
|
|
|
from rest_framework_gis.serializers import GeoFeatureModelSerializer
|
|
|
|
|
|
from sqlalchemy.sql.functions import mode
|
2025-08-30 02:20:25 +09:00
|
|
|
|
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
|
2022-03-14 10:50:11 +05:30
|
|
|
|
from drf_extra_fields.fields import Base64ImageField
|
|
|
|
|
|
|
2022-05-12 02:15:36 +05:30
|
|
|
|
#from django.contrib.auth.models import User
|
|
|
|
|
|
from .models import CustomUser
|
2022-04-27 15:47:37 +05:30
|
|
|
|
from django.contrib.auth import authenticate
|
|
|
|
|
|
|
2022-05-12 02:15:36 +05:30
|
|
|
|
from .models import TestModel
|
2024-07-25 00:57:48 +00:00
|
|
|
|
import logging
|
2024-07-26 04:03:15 +00:00
|
|
|
|
from django.shortcuts import get_object_or_404
|
|
|
|
|
|
from django.utils import timezone
|
|
|
|
|
|
from datetime import datetime, date
|
2022-05-12 02:15:36 +05:30
|
|
|
|
|
2024-11-12 07:19:18 +09:00
|
|
|
|
|
2024-07-25 00:57:48 +00:00
|
|
|
|
logger = logging.getLogger(__name__)
|
2022-04-27 15:47:37 +05:30
|
|
|
|
|
|
|
|
|
|
class LocationCatSerializer(serializers.ModelSerializer):
|
|
|
|
|
|
class Meta:
|
2025-08-30 02:20:25 +09:00
|
|
|
|
model=Location2025
|
2022-04-27 15:47:37 +05:30
|
|
|
|
fields=['category',]
|
|
|
|
|
|
|
2022-03-14 10:50:11 +05:30
|
|
|
|
|
2025-08-30 04:12:55 +09:00
|
|
|
|
class LocationSerializer(serializers.ModelSerializer):
|
2025-08-27 15:01:06 +09:00
|
|
|
|
# evaluation_valueに基づくインタラクション情報を追加
|
|
|
|
|
|
interaction_type = serializers.SerializerMethodField()
|
|
|
|
|
|
requires_photo = serializers.SerializerMethodField()
|
|
|
|
|
|
requires_qr_code = serializers.SerializerMethodField()
|
|
|
|
|
|
interaction_instructions = serializers.SerializerMethodField()
|
|
|
|
|
|
|
2025-08-30 04:12:55 +09:00
|
|
|
|
# 追加フィールドのカスタムシリアライズ
|
|
|
|
|
|
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()
|
|
|
|
|
|
|
2025-09-02 05:02:16 +09:00
|
|
|
|
# フィールド名の別名対応
|
|
|
|
|
|
webcontent = serializers.SerializerMethodField()
|
|
|
|
|
|
zip = serializers.SerializerMethodField()
|
|
|
|
|
|
s3_name = serializers.SerializerMethodField()
|
|
|
|
|
|
|
|
|
|
|
|
def get_webcontent(self, obj):
|
|
|
|
|
|
"""websiteフィールドをwebcontentとして返す"""
|
|
|
|
|
|
return obj.website
|
|
|
|
|
|
|
|
|
|
|
|
def get_zip(self, obj):
|
|
|
|
|
|
"""zip_codeフィールドをzipとして返す"""
|
|
|
|
|
|
return obj.zip_code
|
|
|
|
|
|
|
|
|
|
|
|
def get_s3_name(self, obj):
|
|
|
|
|
|
"""S3ファイル名を生成して返す(event_nameのローマ字表記)"""
|
|
|
|
|
|
if not obj.event:
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
# NewEvent2のs3_nameフィールドを参照
|
|
|
|
|
|
if hasattr(obj.event, 's3_name') and obj.event.s3_name:
|
|
|
|
|
|
return obj.event.s3_name
|
|
|
|
|
|
|
|
|
|
|
|
# フォールバック: event_nameをローマ字に変換
|
|
|
|
|
|
if obj.event.event_name:
|
|
|
|
|
|
s3_name = self._convert_japanese_to_romaji(obj.event.event_name)
|
|
|
|
|
|
return s3_name
|
|
|
|
|
|
|
|
|
|
|
|
# 最終フォールバック: イベントコード
|
|
|
|
|
|
return obj.event.event_code
|
|
|
|
|
|
|
|
|
|
|
|
def _convert_japanese_to_romaji(self, japanese_text):
|
|
|
|
|
|
"""日本語をローマ字に変換する"""
|
|
|
|
|
|
# 基本的な日本語→ローマ字変換テーブル
|
|
|
|
|
|
conversion_table = {
|
|
|
|
|
|
# 地名
|
|
|
|
|
|
'岐阜': 'gifu',
|
|
|
|
|
|
'大垣': 'ogaki',
|
|
|
|
|
|
'多治見': 'tajimi',
|
|
|
|
|
|
'関': 'seki',
|
|
|
|
|
|
'中津川': 'nakatsugawa',
|
|
|
|
|
|
'美濃': 'mino',
|
|
|
|
|
|
'瑞浪': 'mizunami',
|
|
|
|
|
|
'羽島': 'hashima',
|
|
|
|
|
|
'恵那': 'ena',
|
|
|
|
|
|
'美濃加茂': 'minokamo',
|
|
|
|
|
|
'土岐': 'toki',
|
|
|
|
|
|
'各務原': 'kakamigahara',
|
|
|
|
|
|
'可児': 'kani',
|
|
|
|
|
|
'山県': 'yamagata',
|
|
|
|
|
|
'瑞穂': 'mizuho',
|
|
|
|
|
|
'飛騨': 'hida',
|
|
|
|
|
|
'本巣': 'motosu',
|
|
|
|
|
|
'郡上': 'gujo',
|
|
|
|
|
|
'下呂': 'gero',
|
|
|
|
|
|
'海津': 'kaizu',
|
|
|
|
|
|
|
|
|
|
|
|
# 一般的な単語
|
|
|
|
|
|
'ロゲイニング': 'rogaining',
|
|
|
|
|
|
'ロゲ': 'roge',
|
|
|
|
|
|
'イベント': 'event',
|
|
|
|
|
|
'大会': 'taikai',
|
|
|
|
|
|
'競技': 'kyogi',
|
|
|
|
|
|
'スポーツ': 'sports',
|
|
|
|
|
|
'アウトドア': 'outdoor',
|
|
|
|
|
|
'ハイキング': 'hiking',
|
|
|
|
|
|
'ウォーキング': 'walking',
|
|
|
|
|
|
'ランニング': 'running',
|
|
|
|
|
|
'マラソン': 'marathon',
|
|
|
|
|
|
'トレイル': 'trail',
|
|
|
|
|
|
'オリエンテーリング': 'orienteering',
|
|
|
|
|
|
'ナビゲーション': 'navigation',
|
|
|
|
|
|
'チェックポイント': 'checkpoint',
|
|
|
|
|
|
'ゴール': 'goal',
|
|
|
|
|
|
'スタート': 'start',
|
|
|
|
|
|
'チーム': 'team',
|
|
|
|
|
|
'ファミリー': 'family',
|
|
|
|
|
|
'ジュニア': 'junior',
|
|
|
|
|
|
'シニア': 'senior',
|
|
|
|
|
|
'初心者': 'beginner',
|
|
|
|
|
|
'上級者': 'advanced',
|
|
|
|
|
|
'中級者': 'intermediate',
|
|
|
|
|
|
|
|
|
|
|
|
# 年号
|
|
|
|
|
|
'2025': '2025',
|
|
|
|
|
|
'2025': '2025',
|
|
|
|
|
|
'2024': '2024',
|
|
|
|
|
|
'2024': '2024',
|
|
|
|
|
|
'2026': '2026',
|
|
|
|
|
|
'2026': '2026',
|
|
|
|
|
|
'2027': '2027',
|
|
|
|
|
|
'2027': '2027',
|
|
|
|
|
|
'2028': '2028',
|
|
|
|
|
|
'2028': '2028',
|
|
|
|
|
|
'2029': '2029',
|
|
|
|
|
|
'2029': '2029',
|
|
|
|
|
|
'2030': '2030',
|
|
|
|
|
|
'2030': '2030',
|
|
|
|
|
|
|
|
|
|
|
|
# 月
|
|
|
|
|
|
'1月': 'january',
|
|
|
|
|
|
'2月': 'february',
|
|
|
|
|
|
'3月': 'march',
|
|
|
|
|
|
'4月': 'april',
|
|
|
|
|
|
'5月': 'may',
|
|
|
|
|
|
'6月': 'june',
|
|
|
|
|
|
'7月': 'july',
|
|
|
|
|
|
'8月': 'august',
|
|
|
|
|
|
'9月': 'september',
|
|
|
|
|
|
'10月': 'october',
|
|
|
|
|
|
'11月': 'november',
|
|
|
|
|
|
'12月': 'december',
|
|
|
|
|
|
|
|
|
|
|
|
# その他
|
|
|
|
|
|
'春': 'spring',
|
|
|
|
|
|
'夏': 'summer',
|
|
|
|
|
|
'秋': 'autumn',
|
|
|
|
|
|
'冬': 'winter',
|
|
|
|
|
|
'第': 'dai',
|
|
|
|
|
|
'回': 'kai',
|
|
|
|
|
|
'杯': 'hai',
|
|
|
|
|
|
'記念': 'kinen',
|
|
|
|
|
|
'特別': 'tokubetsu',
|
|
|
|
|
|
'公式': 'koshiki',
|
|
|
|
|
|
'練習': 'renshu',
|
|
|
|
|
|
'体験': 'taiken'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result = japanese_text.lower()
|
|
|
|
|
|
|
|
|
|
|
|
# 変換テーブルを使用して変換
|
|
|
|
|
|
for japanese, romaji in conversion_table.items():
|
|
|
|
|
|
result = result.replace(japanese, romaji)
|
|
|
|
|
|
|
|
|
|
|
|
# 記号の変換
|
|
|
|
|
|
result = result.replace(' ', '_')
|
|
|
|
|
|
result = result.replace('-', '_')
|
|
|
|
|
|
result = result.replace(' ', '_') # 全角スペース
|
|
|
|
|
|
result = result.replace('・', '_')
|
|
|
|
|
|
result = result.replace('/', '_')
|
|
|
|
|
|
result = result.replace('\\', '_')
|
|
|
|
|
|
result = result.replace('(', '_')
|
|
|
|
|
|
result = result.replace(')', '_')
|
|
|
|
|
|
result = result.replace('(', '_')
|
|
|
|
|
|
result = result.replace(')', '_')
|
|
|
|
|
|
result = result.replace('[', '_')
|
|
|
|
|
|
result = result.replace(']', '_')
|
|
|
|
|
|
result = result.replace('【', '_')
|
|
|
|
|
|
result = result.replace('】', '_')
|
|
|
|
|
|
result = result.replace(':', '_')
|
|
|
|
|
|
result = result.replace(':', '_')
|
|
|
|
|
|
result = result.replace(';', '_')
|
|
|
|
|
|
result = result.replace(';', '_')
|
|
|
|
|
|
result = result.replace(',', '_')
|
|
|
|
|
|
result = result.replace(',', '_')
|
|
|
|
|
|
result = result.replace('.', '_')
|
|
|
|
|
|
result = result.replace('。', '_')
|
|
|
|
|
|
result = result.replace('!', '_')
|
|
|
|
|
|
result = result.replace('!', '_')
|
|
|
|
|
|
result = result.replace('?', '_')
|
|
|
|
|
|
result = result.replace('?', '_')
|
|
|
|
|
|
|
|
|
|
|
|
# 連続するアンダースコアを単一に
|
|
|
|
|
|
while '__' in result:
|
|
|
|
|
|
result = result.replace('__', '_')
|
|
|
|
|
|
|
|
|
|
|
|
# 先頭と末尾のアンダースコアを削除
|
|
|
|
|
|
result = result.strip('_')
|
|
|
|
|
|
|
|
|
|
|
|
# 空文字列の場合は'event'を返す
|
|
|
|
|
|
if not result:
|
|
|
|
|
|
result = 'event'
|
|
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
2022-03-14 10:50:11 +05:30
|
|
|
|
class Meta:
|
2025-08-30 02:20:25 +09:00
|
|
|
|
model=Location2025
|
2025-08-30 04:12:55 +09:00
|
|
|
|
fields=[
|
|
|
|
|
|
# 基本フィールド
|
2025-09-02 05:02:16 +09:00
|
|
|
|
'id', 'cp_number', 'event', 'cp_name', 's3_name','category', 'sub_loc_id', 'subcategory',
|
2025-08-30 04:12:55 +09:00
|
|
|
|
'latitude', 'longitude', 'location', 'address',
|
2025-08-30 04:39:45 +09:00
|
|
|
|
'checkin_point', 'buy_point',
|
2025-08-30 04:12:55 +09:00
|
|
|
|
'checkin_radius', 'auto_checkin',
|
|
|
|
|
|
'shop_closed', 'shop_shutdown', 'opening_hours',
|
2025-09-02 05:02:16 +09:00
|
|
|
|
'phone', 'website', 'facility', 'description', 'remark',
|
|
|
|
|
|
# 住所詳細フィールド
|
|
|
|
|
|
'zip_code', 'prefecture', 'area', 'city',
|
2025-08-30 04:12:55 +09:00
|
|
|
|
# 追加フィールド
|
|
|
|
|
|
'photos', 'videos', 'tags', 'evaluation_value', 'hidden_location',
|
|
|
|
|
|
# 管理フィールド
|
2025-09-02 05:02:16 +09:00
|
|
|
|
'is_active', 'sort_order', 'csv_source_file', 'csv_upload_date', 'csv_upload_user',
|
2025-08-30 04:12:55 +09:00
|
|
|
|
'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',
|
2025-09-02 05:02:16 +09:00
|
|
|
|
'latitude_float', 'longitude_float', 'webcontent', 'zip'
|
2025-08-30 04:12:55 +09:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
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()]
|
2025-08-27 15:01:06 +09:00
|
|
|
|
|
|
|
|
|
|
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":
|
2025-09-02 05:02:16 +09:00
|
|
|
|
return "QRコードをスキャンしてください"
|
2025-08-27 15:01:06 +09:00
|
|
|
|
else:
|
|
|
|
|
|
return "この場所でチェックインしてポイントを獲得してください"
|
2022-03-14 10:50:11 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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__"
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-03-16 17:46:22 +05:30
|
|
|
|
class JPN_main_perfSerializer(serializers.ModelSerializer):
|
2022-03-14 10:50:11 +05:30
|
|
|
|
class Meta:
|
2022-03-16 17:46:22 +05:30
|
|
|
|
model=JpnAdminMainPerf
|
2022-03-30 16:17:48 +05:30
|
|
|
|
fields=['id', 'adm0_en', 'adm0_ja', 'adm0_pcode', 'adm1_en', 'adm1_ja', 'adm1_pcode']
|
2022-03-14 10:50:11 +05:30
|
|
|
|
|
2023-05-26 13:56:26 +05:30
|
|
|
|
# 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']
|
2022-04-05 16:36:21 +05:30
|
|
|
|
|
|
|
|
|
|
|
2023-05-26 13:56:26 +05:30
|
|
|
|
# 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']
|
2022-04-27 15:47:37 +05:30
|
|
|
|
|
|
|
|
|
|
|
2022-06-06 21:15:07 +05:30
|
|
|
|
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']
|
|
|
|
|
|
|
2024-07-26 12:34:54 +00:00
|
|
|
|
class UserRegistrationSerializer(serializers.ModelSerializer):
|
|
|
|
|
|
password = serializers.CharField(write_only=True, required=True, validators=[validate_password])
|
2024-08-02 07:01:32 +00:00
|
|
|
|
password2 = serializers.CharField(write_only=True, required=True, validators=[validate_password])
|
2024-07-26 12:34:54 +00:00
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
|
model = CustomUser
|
2024-08-02 07:01:32 +00:00
|
|
|
|
fields = ('email', 'password', 'password2', 'firstname', 'lastname', 'date_of_birth', 'female')
|
2024-07-26 12:34:54 +00:00
|
|
|
|
extra_kwargs = {
|
|
|
|
|
|
'email': {'required': True},
|
|
|
|
|
|
'firstname': {'required': True},
|
|
|
|
|
|
'lastname': {'required': True},
|
|
|
|
|
|
'date_of_birth': {'required': True},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-02 07:01:32 +00:00
|
|
|
|
def validate(self, attrs):
|
|
|
|
|
|
if attrs['password'] != attrs['password2']:
|
|
|
|
|
|
raise serializers.ValidationError({"password": "Password fields didn't match."})
|
2024-07-26 12:34:54 +00:00
|
|
|
|
|
|
|
|
|
|
try:
|
2024-08-02 07:01:32 +00:00
|
|
|
|
validate_password(attrs['password'])
|
2024-07-26 12:34:54 +00:00
|
|
|
|
except ValidationError as e:
|
|
|
|
|
|
raise serializers.ValidationError({"password": list(e.messages)})
|
2022-06-06 21:15:07 +05:30
|
|
|
|
|
2024-08-02 07:01:32 +00:00
|
|
|
|
return attrs
|
2022-04-27 15:47:37 +05:30
|
|
|
|
|
2024-08-02 07:01:32 +00:00
|
|
|
|
def validate_email(self, value):
|
|
|
|
|
|
if CustomUser.objects.filter(email=value).exists() or TempUser.objects.filter(email=value).exists():
|
|
|
|
|
|
raise serializers.ValidationError("この電子メールアドレスは既に使用されています。")
|
|
|
|
|
|
return value
|
2022-04-27 15:47:37 +05:30
|
|
|
|
|
|
|
|
|
|
def create(self, validated_data):
|
2024-08-02 07:01:32 +00:00
|
|
|
|
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
|
2024-07-26 12:34:54 +00:00
|
|
|
|
|
|
|
|
|
|
class TempUserRegistrationSerializer(serializers.ModelSerializer):
|
2024-08-02 14:21:50 +00:00
|
|
|
|
password = serializers.CharField(write_only=True)
|
|
|
|
|
|
|
2024-07-26 12:34:54 +00:00
|
|
|
|
class Meta:
|
|
|
|
|
|
model = TempUser
|
2024-08-02 14:21:50 +00:00
|
|
|
|
fields = ('email', 'password', 'firstname', 'lastname', 'date_of_birth', 'female')
|
2024-07-26 12:34:54 +00:00
|
|
|
|
|
|
|
|
|
|
def create(self, validated_data):
|
2024-08-02 14:21:50 +00:00
|
|
|
|
# パスワードのハッシュ化はviewで行うので、ここではそのまま保存
|
2024-07-26 12:34:54 +00:00
|
|
|
|
return TempUser.objects.create(**validated_data)
|
|
|
|
|
|
|
2024-08-02 14:21:50 +00:00
|
|
|
|
#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)
|
|
|
|
|
|
|
2022-04-27 15:47:37 +05:30
|
|
|
|
|
|
|
|
|
|
class UserSerializer(serializers.ModelSerializer):
|
|
|
|
|
|
class Meta:
|
2022-05-12 02:15:36 +05:30
|
|
|
|
model = CustomUser
|
2025-09-02 11:40:17 +09:00
|
|
|
|
fields = ('id','email', 'is_rogaining' ,'group', 'zekken_number', 'event_code', 'team_name', 'is_staff')
|
2022-04-27 15:47:37 +05:30
|
|
|
|
|
2022-10-18 14:58:20 +05:30
|
|
|
|
class GolaImageSerializer(serializers.ModelSerializer):
|
|
|
|
|
|
goalimage = Base64ImageField(max_length=None, use_url=True)
|
2024-08-09 23:49:36 +00:00
|
|
|
|
|
2022-10-18 14:58:20 +05:30
|
|
|
|
class Meta:
|
|
|
|
|
|
model = GoalImages
|
|
|
|
|
|
fields="__all__"
|
2022-11-05 14:31:55 +05:30
|
|
|
|
|
2024-08-09 23:49:36 +00:00
|
|
|
|
def get_goalimage_url_old(self, car):
|
2022-11-05 14:31:55 +05:30
|
|
|
|
request = self.context.get('request')
|
|
|
|
|
|
photo_url = GoalImages.goalimage.url
|
|
|
|
|
|
return request.build_absolute_uri(photo_url)
|
2022-10-18 14:58:20 +05:30
|
|
|
|
|
2024-08-09 23:49:36 +00:00
|
|
|
|
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
|
|
|
|
|
|
|
2022-11-05 18:11:00 +05:30
|
|
|
|
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)
|
|
|
|
|
|
|
2022-04-27 15:47:37 +05:30
|
|
|
|
|
2022-06-08 21:40:43 +05:30
|
|
|
|
class RogUserSerializer(serializers.ModelSerializer):
|
|
|
|
|
|
class Meta:
|
|
|
|
|
|
model = RogUser
|
|
|
|
|
|
fields = ('id','user', 'paid',)
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-04-27 15:47:37 +05:30
|
|
|
|
class LoginUserSerializer(serializers.Serializer):
|
2024-08-02 07:01:32 +00:00
|
|
|
|
#email = serializers.CharField()
|
|
|
|
|
|
email = serializers.EmailField()
|
2022-04-27 15:47:37 +05:30
|
|
|
|
password = serializers.CharField()
|
|
|
|
|
|
|
|
|
|
|
|
def validate(self, data):
|
2024-08-02 07:01:32 +00:00
|
|
|
|
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:
|
2025-09-05 23:20:10 +09:00
|
|
|
|
from .models import CustomUser
|
|
|
|
|
|
user_obj = CustomUser.objects.get(email=email)
|
2024-08-02 07:01:32 +00:00
|
|
|
|
raise serializers.ValidationError("Incorrect password.")
|
2025-09-05 23:20:10 +09:00
|
|
|
|
except CustomUser.DoesNotExist:
|
2024-08-02 07:01:32 +00:00
|
|
|
|
raise serializers.ValidationError("User with this email does not exist.")
|
|
|
|
|
|
else:
|
|
|
|
|
|
raise serializers.ValidationError("Must include 'email' and 'password'.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-05-12 02:15:36 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UseractionsSerializer(serializers.ModelSerializer):
|
|
|
|
|
|
|
2022-06-22 22:38:09 +05:30
|
|
|
|
user = UserSerializer(read_only=True)
|
2022-05-12 02:15:36 +05:30
|
|
|
|
#location = LocationSerializer(read_only=True)
|
2022-06-22 22:39:30 +05:30
|
|
|
|
location = serializers.RelatedField(source='Location', read_only=True)
|
2022-05-12 02:15:36 +05:30
|
|
|
|
#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')
|
|
|
|
|
|
|
2022-06-08 21:40:43 +05:30
|
|
|
|
class LocationEventNameSerializer(serializers.ModelSerializer):
|
|
|
|
|
|
class Meta:
|
2025-08-30 02:20:25 +09:00
|
|
|
|
model = Location2025
|
2022-06-08 21:40:43 +05:30
|
|
|
|
fields = ('id', 'event_name',)
|
|
|
|
|
|
|
2022-06-13 01:55:45 +05:30
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
2022-05-12 02:15:36 +05:30
|
|
|
|
|
|
|
|
|
|
class TestSerialiser(serializers.ModelSerializer):
|
|
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
|
model = TestModel
|
2022-10-06 17:42:12 +05:30
|
|
|
|
fields = ('id', 'testbane', 'wanttogo', 'like', 'checkin')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ChangePasswordSerializer(serializers.Serializer):
|
|
|
|
|
|
model = CustomUser
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
Serializer for password change endpoint.
|
|
|
|
|
|
"""
|
|
|
|
|
|
old_password = serializers.CharField(required=True)
|
2023-05-03 16:12:53 +05:30
|
|
|
|
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()
|
2024-07-24 00:38:32 +00:00
|
|
|
|
return user
|
|
|
|
|
|
|
2024-07-25 00:57:48 +00:00
|
|
|
|
class NewCategorySerializer(serializers.ModelSerializer):
|
|
|
|
|
|
class Meta:
|
|
|
|
|
|
model = NewCategory
|
2024-08-04 18:56:11 +00:00
|
|
|
|
fields = ['id','category_name', 'category_number', 'duration', 'num_of_member', 'family', 'female']
|
|
|
|
|
|
#fields = ['id','category_name', 'category_number']
|
2024-07-25 00:57:48 +00:00
|
|
|
|
|
|
|
|
|
|
class NewEvent2Serializer(serializers.ModelSerializer):
|
2025-08-27 15:01:06 +09:00
|
|
|
|
# API変更要求書対応: deadline_datetime フィールド追加
|
|
|
|
|
|
deadline_datetime = serializers.DateTimeField(source='deadlineDateTime', read_only=True)
|
2025-08-28 11:59:46 +09:00
|
|
|
|
# Supervisor web app compatibility
|
|
|
|
|
|
code = serializers.CharField(source='event_name', read_only=True)
|
|
|
|
|
|
name = serializers.CharField(source='event_name', read_only=True)
|
2025-08-27 15:01:06 +09:00
|
|
|
|
|
2024-07-25 00:57:48 +00:00
|
|
|
|
class Meta:
|
|
|
|
|
|
model = NewEvent2
|
2025-08-27 15:01:06 +09:00
|
|
|
|
fields = [
|
|
|
|
|
|
'id', 'event_name', 'start_datetime', 'end_datetime',
|
|
|
|
|
|
'deadlineDateTime', 'deadline_datetime', 'status', 'public',
|
|
|
|
|
|
'hour_3', 'hour_5', 'class_general', 'class_family',
|
2025-08-28 11:59:46 +09:00
|
|
|
|
'class_solo_male', 'class_solo_female',
|
|
|
|
|
|
'code', 'name' # Supervisor compatibility
|
2025-08-27 15:01:06 +09:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
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
|
2024-07-25 00:57:48 +00:00
|
|
|
|
|
2024-07-24 00:38:32 +00:00
|
|
|
|
class NewEventSerializer(serializers.ModelSerializer):
|
|
|
|
|
|
class Meta:
|
|
|
|
|
|
model = NewEvent
|
|
|
|
|
|
fields = ['event_name', 'start_datetime', 'end_datetime']
|
|
|
|
|
|
|
|
|
|
|
|
class TeamSerializer(serializers.ModelSerializer):
|
2024-07-26 04:03:15 +00:00
|
|
|
|
category = serializers.PrimaryKeyRelatedField(queryset=NewCategory.objects.all())
|
2024-07-25 00:57:48 +00:00
|
|
|
|
|
2024-07-26 04:03:15 +00:00
|
|
|
|
#category = serializers.IntegerField()
|
|
|
|
|
|
#category = NewCategorySerializer(read_only=True)
|
2024-07-25 00:57:48 +00:00
|
|
|
|
#category_id = serializers.PrimaryKeyRelatedField(
|
|
|
|
|
|
# queryset=NewCategory.objects.all(),
|
|
|
|
|
|
# source='category',
|
|
|
|
|
|
# write_only=True
|
|
|
|
|
|
#)
|
|
|
|
|
|
owner = serializers.PrimaryKeyRelatedField(read_only=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-07-24 00:38:32 +00:00
|
|
|
|
class Meta:
|
|
|
|
|
|
model = Team
|
2024-08-04 18:56:11 +00:00
|
|
|
|
fields = ['id','team_name', 'category', 'owner']
|
|
|
|
|
|
read_only_fields = ['id', 'owner']
|
2024-07-25 00:57:48 +00:00
|
|
|
|
|
|
|
|
|
|
def to_representation(self, instance):
|
|
|
|
|
|
ret = super().to_representation(instance)
|
2024-07-26 04:03:15 +00:00
|
|
|
|
if instance.category:
|
|
|
|
|
|
ret['category'] = {
|
|
|
|
|
|
'id': instance.category.id,
|
|
|
|
|
|
'category_name': instance.category.category_name,
|
2024-08-04 18:56:11 +00:00
|
|
|
|
'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
|
2024-07-26 04:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
else:
|
|
|
|
|
|
ret['category'] = None
|
2024-07-25 00:57:48 +00:00
|
|
|
|
ret['owner'] = CustomUserSerializer(instance.owner).data
|
|
|
|
|
|
return ret
|
|
|
|
|
|
|
|
|
|
|
|
def validate_category(self, value):
|
2024-07-26 04:03:15 +00:00
|
|
|
|
if not isinstance(value, NewCategory):
|
|
|
|
|
|
raise serializers.ValidationError("Invalid category")
|
2024-07-25 00:57:48 +00:00
|
|
|
|
return value
|
|
|
|
|
|
|
2024-07-26 04:03:15 +00:00
|
|
|
|
#if not NewCategory.objects.filter(id=value).exists():
|
|
|
|
|
|
# raise serializers.ValidationError("Invalid category ID")
|
|
|
|
|
|
#return value
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-07-25 00:57:48 +00:00
|
|
|
|
def create(self, validated_data):
|
2024-07-26 04:03:15 +00:00
|
|
|
|
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
|
2024-07-25 00:57:48 +00:00
|
|
|
|
|
|
|
|
|
|
#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):
|
2024-07-26 04:03:15 +00:00
|
|
|
|
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)
|
2024-07-24 00:38:32 +00:00
|
|
|
|
|
|
|
|
|
|
class CategorySerializer(serializers.ModelSerializer):
|
|
|
|
|
|
class Meta:
|
|
|
|
|
|
model = Category
|
|
|
|
|
|
fields = ['category_name', 'category_number', 'duration', 'num_of_member', 'family', 'female']
|
|
|
|
|
|
|
|
|
|
|
|
class EntrySerializer(serializers.ModelSerializer):
|
2024-07-25 00:57:48 +00:00
|
|
|
|
team = serializers.PrimaryKeyRelatedField(queryset=Team.objects.all())
|
2024-07-26 04:03:15 +00:00
|
|
|
|
event = serializers.PrimaryKeyRelatedField(queryset=NewEvent2.objects.all())
|
2024-07-25 00:57:48 +00:00
|
|
|
|
category = serializers.PrimaryKeyRelatedField(queryset=NewCategory.objects.all())
|
|
|
|
|
|
owner = serializers.PrimaryKeyRelatedField(read_only=True)
|
2025-08-31 17:51:41 +09:00
|
|
|
|
date = serializers.DateTimeField(required=False, allow_null=True) # DateTimeFieldを使用
|
2024-08-04 18:56:11 +00:00
|
|
|
|
zekken_number = serializers.IntegerField()
|
2024-07-26 04:03:15 +00:00
|
|
|
|
|
2024-07-24 00:38:32 +00:00
|
|
|
|
class Meta:
|
|
|
|
|
|
model = Entry
|
2025-08-27 15:01:06 +09:00
|
|
|
|
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']
|
2024-07-26 04:03:15 +00:00
|
|
|
|
|
|
|
|
|
|
def validate_date(self, value):
|
|
|
|
|
|
if isinstance(value, str):
|
|
|
|
|
|
try:
|
2025-08-31 17:51:41 +09:00
|
|
|
|
# 文字列をdatetimeオブジェクトに変換
|
2024-07-26 04:03:15 +00:00
|
|
|
|
value = datetime.strptime(value, "%Y-%m-%d")
|
|
|
|
|
|
except ValueError:
|
|
|
|
|
|
raise serializers.ValidationError("Invalid date format. Use YYYY-MM-DD.")
|
|
|
|
|
|
|
2025-08-31 17:51:41 +09:00
|
|
|
|
if isinstance(value, date) and not isinstance(value, datetime):
|
|
|
|
|
|
# dateオブジェクトをdatetimeオブジェクトに変換
|
2024-07-26 04:03:15 +00:00
|
|
|
|
value = datetime.combine(value, datetime.min.time())
|
|
|
|
|
|
|
2025-08-31 17:51:41 +09:00
|
|
|
|
if isinstance(value, datetime) and timezone.is_naive(value):
|
2024-07-26 04:03:15 +00:00
|
|
|
|
return timezone.make_aware(value, timezone.get_current_timezone())
|
|
|
|
|
|
return value
|
|
|
|
|
|
|
2024-07-25 00:57:48 +00:00
|
|
|
|
def validate_team(self, value):
|
|
|
|
|
|
if not value.members.exists():
|
|
|
|
|
|
raise serializers.ValidationError("チームにメンバーが登録されていません。")
|
|
|
|
|
|
return value
|
2024-07-24 00:38:32 +00:00
|
|
|
|
|
2024-07-26 04:03:15 +00:00
|
|
|
|
def validate(self, data):
|
|
|
|
|
|
team = data.get('team')
|
|
|
|
|
|
event = data.get('event')
|
|
|
|
|
|
category = data.get('category')
|
|
|
|
|
|
entry_date = data.get('date')
|
2024-09-03 13:19:30 +00:00
|
|
|
|
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()
|
|
|
|
|
|
|
2024-10-21 12:48:42 +00:00
|
|
|
|
logger.debug("test-0")
|
|
|
|
|
|
logger.debug(f"==== start:{event.start_datetime.date()} <= entry_date : {entry_date} <= end:{event.end_datetime.date()} ?? ====")
|
2024-09-03 13:19:30 +00:00
|
|
|
|
|
|
|
|
|
|
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()}の間である必要があります。")
|
|
|
|
|
|
|
2024-10-21 12:48:42 +00:00
|
|
|
|
logger.debug("test-1")
|
2024-09-03 13:19:30 +00:00
|
|
|
|
|
2024-10-21 12:48:42 +00:00
|
|
|
|
try:
|
|
|
|
|
|
logger.debug(f"Parsed data: team={team}, event={event}, category={category}, ")
|
|
|
|
|
|
|
|
|
|
|
|
owner = self.context['request'].user
|
|
|
|
|
|
zekken_number = data.get('zekken_number')
|
2024-07-26 04:03:15 +00:00
|
|
|
|
|
2024-10-21 12:48:42 +00:00
|
|
|
|
logger.debug(f"entry_date={entry_date}, owner={owner}, zekken_number={zekken_number}")
|
2024-08-09 23:49:36 +00:00
|
|
|
|
|
2024-10-21 12:48:42 +00:00
|
|
|
|
except Exception:
|
|
|
|
|
|
raise serializers.ValidationError(f"何らかのエラーが発生しました")
|
2024-08-09 23:49:36 +00:00
|
|
|
|
|
2024-07-26 04:03:15 +00:00
|
|
|
|
# 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("既に登録済みです。")
|
|
|
|
|
|
|
2024-08-04 18:56:11 +00:00
|
|
|
|
# Validate zekken_number
|
|
|
|
|
|
if zekken_number is not None:
|
|
|
|
|
|
if zekken_number <= 0:
|
|
|
|
|
|
raise serializers.ValidationError("ゼッケン番号は正の整数である必要があります。")
|
2024-08-09 23:49:36 +00:00
|
|
|
|
# if Entry.objects.filter(event=event, zekken_number=zekken_number).exists():
|
|
|
|
|
|
# raise serializers.ValidationError("このゼッケン番号は既に使用されています。")
|
2024-08-04 18:56:11 +00:00
|
|
|
|
|
2024-07-26 04:03:15 +00:00
|
|
|
|
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)
|
|
|
|
|
|
|
2024-07-25 00:57:48 +00:00
|
|
|
|
def to_representation(self, instance):
|
|
|
|
|
|
ret = super().to_representation(instance)
|
2024-07-26 12:34:54 +00:00
|
|
|
|
ret['team'] = TeamSerializer(instance.team).data
|
|
|
|
|
|
ret['event'] = NewEvent2Serializer(instance.event).data
|
|
|
|
|
|
ret['category'] = NewCategorySerializer(instance.category).data
|
|
|
|
|
|
ret['owner'] = CustomUserSerializer(instance.owner).data
|
2024-09-03 13:19:30 +00:00
|
|
|
|
|
|
|
|
|
|
if isinstance(ret['date'], datetime):
|
|
|
|
|
|
ret['date'] = ret['date'].date().isoformat()
|
|
|
|
|
|
elif isinstance(ret['date'], date):
|
|
|
|
|
|
ret['date'] = ret['date'].isoformat()
|
2024-07-25 00:57:48 +00:00
|
|
|
|
return ret
|
2024-07-24 00:38:32 +00:00
|
|
|
|
|
2024-07-26 12:34:54 +00:00
|
|
|
|
#if isinstance(ret['date'], datetime):
|
|
|
|
|
|
# ret['date'] = ret['date'].date().isoformat()
|
|
|
|
|
|
#return ret
|
|
|
|
|
|
|
2024-07-26 04:03:15 +00:00
|
|
|
|
#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
|
|
|
|
|
|
|
2024-07-24 00:38:32 +00:00
|
|
|
|
class CustomUserSerializer(serializers.ModelSerializer):
|
|
|
|
|
|
class Meta:
|
|
|
|
|
|
model = CustomUser
|
2025-09-02 11:40:17 +09:00
|
|
|
|
fields = ['id','email', 'firstname', 'lastname', 'date_of_birth', 'female', 'is_staff']
|
2024-07-26 12:34:54 +00:00
|
|
|
|
read_only_fields = ['id','email']
|
2024-07-25 00:57:48 +00:00
|
|
|
|
|
|
|
|
|
|
class TeamDetailSerializer(serializers.ModelSerializer):
|
|
|
|
|
|
category = NewCategorySerializer(read_only=True)
|
|
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
|
model = Team
|
|
|
|
|
|
fields = ['id', 'zekken_number', 'team_name', 'category']
|
2024-07-24 00:38:32 +00:00
|
|
|
|
|
2025-09-02 11:40:17 +09:00
|
|
|
|
class UserDetailSerializer(serializers.ModelSerializer):
|
2025-08-31 20:05:27 +09:00
|
|
|
|
event_date = serializers.SerializerMethodField()
|
|
|
|
|
|
last_goal_time = serializers.SerializerMethodField()
|
|
|
|
|
|
|
2024-07-24 00:38:32 +00:00
|
|
|
|
class Meta:
|
|
|
|
|
|
model = CustomUser
|
2025-09-02 11:40:17 +09:00
|
|
|
|
fields = ['id','email', 'firstname', 'lastname', 'date_of_birth', 'female', 'is_rogaining', 'zekken_number', 'event_code', 'team_name', 'group', 'is_staff', 'event_date', 'last_goal_time']
|
2024-07-25 00:57:48 +00:00
|
|
|
|
read_only_fields = ('id', 'email')
|
2025-08-31 20:05:27 +09:00
|
|
|
|
|
|
|
|
|
|
def get_event_date(self, obj):
|
|
|
|
|
|
"""イベント日付を取得(ハードコーディングの値を返す)"""
|
|
|
|
|
|
# ハードコーディングされた日付をDateTimeとして返す
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
return datetime(2025, 5, 17).isoformat()
|
|
|
|
|
|
|
|
|
|
|
|
def get_last_goal_time(self, obj):
|
|
|
|
|
|
"""最後のゴール時間を取得"""
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
return datetime(2025, 1, 24, 22, 45, 4).isoformat()
|
2024-07-25 00:57:48 +00:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
2024-07-26 04:03:15 +00:00
|
|
|
|
class MemberCreationSerializer(serializers.Serializer):
|
|
|
|
|
|
#email = serializers.EmailField()
|
|
|
|
|
|
email = serializers.EmailField(allow_blank=True, required=False)
|
|
|
|
|
|
|
2024-08-01 07:51:52 +00:00
|
|
|
|
firstname = serializers.CharField(required=False, allow_blank=True)
|
|
|
|
|
|
lastname = serializers.CharField(required=False, allow_blank=True)
|
2024-07-26 04:03:15 +00:00
|
|
|
|
date_of_birth = serializers.DateField(required=False)
|
|
|
|
|
|
female = serializers.BooleanField(required=False)
|
|
|
|
|
|
|
2024-07-24 00:38:32 +00:00
|
|
|
|
|
|
|
|
|
|
class MemberWithUserSerializer(serializers.ModelSerializer):
|
|
|
|
|
|
user = UserSerializer(read_only=True)
|
|
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
|
model = Member
|
|
|
|
|
|
fields = ['user', 'team']
|
2024-07-25 00:57:48 +00:00
|
|
|
|
|
|
|
|
|
|
class MemberSerializer(serializers.ModelSerializer):
|
2024-08-01 07:51:52 +00:00
|
|
|
|
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
|
2025-09-02 11:40:17 +09:00
|
|
|
|
representation['is_staff'] = instance.user.is_staff
|
2024-08-01 07:51:52 +00:00
|
|
|
|
return representation
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MemberSerializerOld(serializers.ModelSerializer):
|
2024-07-26 04:03:15 +00:00
|
|
|
|
user = CustomUserSerializer(read_only=True)
|
2024-08-01 07:51:52 +00:00
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-07-26 12:34:54 +00:00
|
|
|
|
#team = TeamDetailSerializer(read_only=True)
|
2024-07-25 00:57:48 +00:00
|
|
|
|
|
|
|
|
|
|
#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
|
2024-08-01 07:51:52 +00:00
|
|
|
|
fields = ['id','email','firstname','lastname','date_of_birth','female']
|
|
|
|
|
|
#read_only_fields = ['id', 'team']
|
2024-07-26 04:03:15 +00:00
|
|
|
|
|
2024-07-25 00:57:48 +00:00
|
|
|
|
|
2024-07-26 04:03:15 +00:00
|
|
|
|
'''
|
2024-07-25 00:57:48 +00:00
|
|
|
|
def create(self, validated_data):
|
|
|
|
|
|
team = validated_data['team']
|
|
|
|
|
|
email = validated_data.get('email')
|
|
|
|
|
|
|
2024-07-26 04:03:15 +00:00
|
|
|
|
if email.startswith('dummy_'):
|
|
|
|
|
|
user, _ = CustomUser.objects.get_or_create(
|
|
|
|
|
|
email=email,
|
|
|
|
|
|
defaults={**user_data, 'is_active': True}
|
|
|
|
|
|
)
|
2024-07-25 00:57:48 +00:00
|
|
|
|
else:
|
2024-07-26 04:03:15 +00:00
|
|
|
|
user, _ = CustomUser.objects.get_or_create(
|
|
|
|
|
|
email=email,
|
|
|
|
|
|
defaults={**user_data, 'is_active': False}
|
2024-07-25 00:57:48 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
2024-07-26 04:03:15 +00:00
|
|
|
|
member = Member.objects.create(user=user, **validated_data)
|
2024-07-25 00:57:48 +00:00
|
|
|
|
return member
|
2024-07-26 04:03:15 +00:00
|
|
|
|
'''
|
|
|
|
|
|
|
2024-08-01 07:51:52 +00:00
|
|
|
|
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
|
|
|
|
|
|
|
2024-07-25 00:57:48 +00:00
|
|
|
|
|
|
|
|
|
|
def update(self, instance, validated_data):
|
|
|
|
|
|
user_data = validated_data.pop('user', {})
|
|
|
|
|
|
user = instance.user
|
|
|
|
|
|
|
2024-07-26 12:34:54 +00:00
|
|
|
|
for attr, value in user_data.items():
|
|
|
|
|
|
setattr(user, attr, value)
|
|
|
|
|
|
user.save()
|
2024-07-25 00:57:48 +00:00
|
|
|
|
|
|
|
|
|
|
return super().update(instance, validated_data)
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-07-26 12:34:54 +00:00
|
|
|
|
#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)
|
|
|
|
|
|
|
2024-08-01 07:51:52 +00:00
|
|
|
|
def to_representation(self, instance):
|
|
|
|
|
|
representation = super().to_representation(instance)
|
|
|
|
|
|
representation['email'] = instance.user.email
|
|
|
|
|
|
return representation
|
|
|
|
|
|
|
|
|
|
|
|
'''
|
2024-07-26 12:34:54 +00:00
|
|
|
|
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']
|
|
|
|
|
|
}
|
2024-08-01 07:51:52 +00:00
|
|
|
|
'''
|
2024-07-25 00:57:48 +00:00
|
|
|
|
|
|
|
|
|
|
class EntryMemberSerializer(serializers.ModelSerializer):
|
|
|
|
|
|
class Meta:
|
|
|
|
|
|
model = EntryMember
|
|
|
|
|
|
fields = ['id', 'entry', 'member', 'is_temporary']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TempUserSerializer(serializers.ModelSerializer):
|
|
|
|
|
|
class Meta:
|
|
|
|
|
|
model = TempUser
|
2024-07-26 12:34:54 +00:00
|
|
|
|
#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']
|
2024-07-25 00:57:48 +00:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
2024-08-02 14:21:50 +00:00
|
|
|
|
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
|
2024-07-25 00:57:48 +00:00
|
|
|
|
|
2024-08-09 23:49:36 +00:00
|
|
|
|
class UserLastGoalTimeSerializer(serializers.Serializer):
|
|
|
|
|
|
user_email = serializers.EmailField()
|
|
|
|
|
|
last_goal_time = serializers.DateTimeField()
|
|
|
|
|
|
|
2025-01-22 08:19:49 +00:00
|
|
|
|
class LoginUserSerializer_old(serializers.Serializer):
|
2024-11-12 07:19:18 +09:00
|
|
|
|
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('認証情報が正しくありません。')
|
2025-08-27 15:01:06 +09:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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を超えています")
|
2024-11-12 07:19:18 +09:00
|
|
|
|
|
2025-08-27 15:01:06 +09:00
|
|
|
|
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()
|