diff --git a/config/settings.py b/config/settings.py index f8034db..7a89a6c 100644 --- a/config/settings.py +++ b/config/settings.py @@ -176,7 +176,8 @@ REST_FRAMEWORK = { } -FRONTEND_URL = 'http://rogainig.intranet.sumasen.net/' # フロントエンドのURLに適宜変更してください +#FRONTEND_URL = 'https://rogaining.intranet.sumasen.net' # フロントエンドのURLに適宜変更してください +FRONTEND_URL = 'https://rogaining.sumasen.net' # フロントエンドのURLに適宜変更してください # この設定により、メールは実際には送信されず、代わりにコンソールに出力されます。 EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' @@ -186,10 +187,12 @@ EMAIL_PORT = 587 EMAIL_USE_TLS = True EMAIL_HOST_USER = 'rogaining@gifuai.net' EMAIL_HOST_PASSWORD = 'ctcpy9823"x~' -DEFAULT_FROM_EMAIL = 'info@gifuai.net' +DEFAULT_FROM_EMAIL = 'rogaining@gifuai.net' -APP_DOWNLOAD_LINK = 'http://your-app-download-link.com' -SERVICE_NAME = '岐阜ロゲ' +APP_DOWNLOAD_LINK = 'https://apps.apple.com/jp/app/%E5%B2%90%E9%98%9C%E3%83%8A%E3%83%93/id6444221792' +ANDROID_DOWNLOAD_LINK = 'https://play.google.com/store/apps/details?id=com.dvox.gifunavi&hl=ja' + +SERVICE_NAME = '岐阜ナビ(岐阜ロゲのアプリ)' # settings.py DEFAULT_CHARSET = 'utf-8' diff --git a/rog/migrations/0040_auto_20240801_1310.py b/rog/migrations/0040_auto_20240801_1310.py new file mode 100644 index 0000000..aaf6414 --- /dev/null +++ b/rog/migrations/0040_auto_20240801_1310.py @@ -0,0 +1,33 @@ +# Generated by Django 3.2.9 on 2024-08-01 04:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('rog', '0039_auto_20240726_1508'), + ] + + operations = [ + migrations.AddField( + model_name='member', + name='date_of_birth', + field=models.DateField(blank=True, null=True), + ), + migrations.AddField( + model_name='member', + name='female', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='member', + name='firstname', + field=models.CharField(blank=True, max_length=255, null=True), + ), + migrations.AddField( + model_name='member', + name='lastname', + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/rog/models.py b/rog/models.py index da4944f..30ba2e3 100644 --- a/rog/models.py +++ b/rog/models.py @@ -301,6 +301,11 @@ class Team(models.Model): class Member(models.Model): team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name='members') user = models.ForeignKey(CustomUser, on_delete=models.CASCADE) + firstname = models.CharField(max_length=255, blank=True, null=True) + lastname = models.CharField(max_length=255, blank=True, null=True) + date_of_birth = models.DateField(null=True, blank=True) + female = models.BooleanField(default=False) + is_temporary = models.BooleanField(default=False) # Akira 2024-7-24 class Meta: diff --git a/rog/serializers.py b/rog/serializers.py index 92b3ade..b81bfcf 100644 --- a/rog/serializers.py +++ b/rog/serializers.py @@ -481,8 +481,8 @@ class MemberCreationSerializer(serializers.Serializer): #email = serializers.EmailField() email = serializers.EmailField(allow_blank=True, required=False) - firstname = serializers.CharField(required=False) - lastname = serializers.CharField(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) @@ -495,7 +495,80 @@ class MemberWithUserSerializer(serializers.ModelSerializer): 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) @@ -506,8 +579,8 @@ class MemberSerializer(serializers.ModelSerializer): class Meta: model = Member - fields = ['id','user','team'] # ,'email','firstname','lastname','date_of_birth','female'] - read_only_fields = ['id', 'team'] + fields = ['id','email','firstname','lastname','date_of_birth','female'] + #read_only_fields = ['id', 'team'] ''' @@ -530,6 +603,25 @@ class MemberSerializer(serializers.ModelSerializer): 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', {}) @@ -551,6 +643,12 @@ class MemberSerializer(serializers.ModelSerializer): #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'] @@ -563,7 +661,7 @@ class MemberSerializer(serializers.ModelSerializer): 'female': user_data['female'], 'team': representation['team'] } - +''' class EntryMemberSerializer(serializers.ModelSerializer): class Meta: diff --git a/rog/templates/activation-template.html b/rog/templates/activation-template.html new file mode 100644 index 0000000..d278c2a --- /dev/null +++ b/rog/templates/activation-template.html @@ -0,0 +1,32 @@ + + + + + + アクティベーション成功 + + + +
+

アクティベーション成功

+

{{ message }}

+
+ + diff --git a/rog/templates/email/verification_email.txt b/rog/templates/email/verification_email.txt index 3108689..5fb3711 100644 --- a/rog/templates/email/verification_email.txt +++ b/rog/templates/email/verification_email.txt @@ -12,6 +12,7 @@ リンクをタップすると、アプリからログインすることができるようになります。 ログインした後は、チーム編成やエントリーまたはチームへの参加を行ってください。 +すでにメンバーとして登録されている場合には、自動的にメンバー登録されます。 なお、リアルロゲイニングは別途申し込みが必要になりますので、ご連絡をお待ちください。 それでは、岐阜ロゲアプリ「岐阜ナビ」をお楽しみください。 diff --git a/rog/templates/verification-template.html b/rog/templates/verification-template.html new file mode 100644 index 0000000..cb60924 --- /dev/null +++ b/rog/templates/verification-template.html @@ -0,0 +1,32 @@ + + + + + + メール確認成功 + + + +
+

メール確認成功

+

{{ message }}

+
+ + diff --git a/rog/urls.py b/rog/urls.py index 3859398..05cdfc2 100644 --- a/rog/urls.py +++ b/rog/urls.py @@ -1,7 +1,7 @@ from sys import prefix from rest_framework import urlpatterns from rest_framework.routers import DefaultRouter -from .views import LocationViewSet, Location_lineViewSet, Location_polygonViewSet, Jpn_Main_PerfViewSet, LocationsInPerf, ExtentForSubPerf, SubPerfInMainPerf, ExtentForMainPerf, LocationsInSubPerf, CatView, RegistrationAPI, LoginAPI, UserAPI, UserActionViewset, UserMakeActionViewset, UserDestinations, UpdateOrder, LocationInBound, DeleteDestination, CustomAreaLocations, GetAllGifuAreas, CustomAreaNames, userDetials, UserTracksViewSet, CatByCity, ChangePasswordView, GoalImageViewSet, CheckinImageViewSet, ExtentForLocations, DeleteAccount, PrivacyView, RegistrationView, TeamViewSet,MemberViewSet,EntryViewSet,RegisterView, VerifyEmailView, NewEventListView,NewEvent2ListView,NewCategoryListView,CategoryListView, MemberUserDetailView, TeamMembersWithUserView,MemberAddView,UserActivationView,RegistrationView,TempUserRegistrationView,ResendInvitationEmailView,update_user_info +from .views import LocationViewSet, Location_lineViewSet, Location_polygonViewSet, Jpn_Main_PerfViewSet, LocationsInPerf, ExtentForSubPerf, SubPerfInMainPerf, ExtentForMainPerf, LocationsInSubPerf, CatView, RegistrationAPI, LoginAPI, UserAPI, UserActionViewset, UserMakeActionViewset, UserDestinations, UpdateOrder, LocationInBound, DeleteDestination, CustomAreaLocations, GetAllGifuAreas, CustomAreaNames, userDetials, UserTracksViewSet, CatByCity, ChangePasswordView, GoalImageViewSet, CheckinImageViewSet, ExtentForLocations, DeleteAccount, PrivacyView, RegistrationView, TeamViewSet,MemberViewSet,EntryViewSet,RegisterView, VerifyEmailView, NewEventListView,NewEvent2ListView,NewCategoryListView,CategoryListView, MemberUserDetailView, TeamMembersWithUserView,MemberAddView,UserActivationView,RegistrationView,TempUserRegistrationView,ResendInvitationEmailView,update_user_info,update_user_detail,ActivateMemberView, ActivateNewMemberView from django.urls import path, include from knox import views as knox_views @@ -34,6 +34,7 @@ router.register(prefix='checkinimage', viewset=CheckinImageViewSet, basename='ch router.register(r'entry', EntryViewSet, basename='entry') router.register(r'teams', TeamViewSet, basename='team') router.register(r'members', MemberViewSet, basename='member') +router.register(r'teams/(?P\d+)/members', MemberViewSet, basename='team-members') # Akira 追加 @@ -86,5 +87,8 @@ urlpatterns += [ #path('register/temp/', RegisterView.as_view(), name='register'), # 古い仮登録 path('reactivate//',ResendInvitationEmailView.as_view(),name='reactivate'), path('userinfo//', update_user_info, name='update_user_info'), - path('userdetail//',update_user_info, name='update_user_detail') + path('userdetail//',update_user_detail, name='update_user_detail'), + path('activate-member///', ActivateMemberView.as_view(), name='activate-member'), + path('activate-new-member///', ActivateNewMemberView.as_view(), name='activate-new-member'), + ] diff --git a/rog/utils.py b/rog/utils.py index ef6651c..a6719da 100644 --- a/rog/utils.py +++ b/rog/utils.py @@ -3,6 +3,8 @@ from django.template.loader import render_to_string from django.conf import settings import logging from django.core.mail import send_mail +from django.urls import reverse +import uuid logger = logging.getLogger(__name__) @@ -46,7 +48,12 @@ def send_verification_email(user, activation_link): # 既にユーザーになっている人にチームへの参加要請メールを出す。 # -def send_team_join_email(sender,user,team,activation_link): +def send_team_join_email(sender,user,team): + activation_link = request.build_absolute_uri( + reverse('activate-member', args=[user.id, team.id]) + ) + logger.info(f"request: {request}") + context = { 'name': user.lastname or user.email, 'invitor': sender.lastname, @@ -63,22 +70,31 @@ def send_team_join_email(sender,user,team,activation_link): # その人がユーザー登録して、ユーザー登録されるとメンバーになる。 # アプリからユーザー登録するため、アプリのダウンロードリンクも送る。 # -def send_invitation_email(sender,user,team,activation_link): +def send_invitation_email(sender,request,user_email,team): + + verification_code = uuid.uuid4() # UUIDを生成 + activation_link = request.build_absolute_uri( + reverse('activate-new-member', args=[verification_code, team.id]) + ) + + context = { - 'name': user.firstname or user.email, + 'name': user_email, 'invitor': sender.lastname, + 'team_name': team.team_name, + 'activation_link': activation_link, 'app_download_link': settings.APP_DOWNLOAD_LINK, 'android_download_link': settings.ANDROID_DOWNLOAD_LINK, } subject, body = load_email_template('invitation_new_email.txt', context) - share_send_email(subject,body,user.email) + share_send_email(subject,body,user_email) # 招待された後にユーザー登録された場合、ヴェリフィケーションでチーム参加登録される。 # -def send_invitaion_and_verification_email(sender, user, team, activation_link): +def send_invitaion_and_verification_email(user, team, activation_link): context = { 'name': user.firstname or user.email, 'activation_link': activation_link, diff --git a/rog/views.py b/rog/views.py index e13c217..f6ba03c 100644 --- a/rog/views.py +++ b/rog/views.py @@ -1,3 +1,4 @@ +import requests from rest_framework import serializers from django.db import IntegrityError from django.urls import reverse @@ -158,6 +159,7 @@ def update_user_detail(request, user_id): setattr(user, field, data[field]) new_value = getattr(user, field) if old_value != new_value: + logger.debug(f"{field}: {old_value} -> {new_value}") updated_fields.append(f"{field}: {old_value} -> {new_value}") logger.debug(f"Fields to be updated: {updated_fields}") @@ -167,6 +169,12 @@ def update_user_detail(request, user_id): logger.info(f"User {user_id} updated. Changed fields: {', '.join(updated_fields)}") + # 更新されたUserデータを使用して関連するMemberのデータを更新 + members = Member.objects.filter(user=user) + for member in members: + member.is_temporary = False + member.save() + serializer = CustomUserSerializer(user) return Response(serializer.data, status=status.HTTP_200_OK) @@ -832,20 +840,8 @@ class NewEventListView(generics.ListAPIView): serializer_class = NewEventSerializer permission_classes = [IsAuthenticated] -def update_external_system(zekken_number, event_code, team_name, class_name, password): - api_url = f"{settings.FRONTEND_URL}/gifuroge/update_team_name" - data = { - "zekken_number": zekken_number, - "new_team_name": team_name, - "event_code": event_code - } - try: - response = requests.post(api_url, json=data) - response.raise_for_status() - return True - except requests.RequestException as e: - logger.error(f"Failed to update external system. Error: {str(e)}") - return False + + class TeamViewSet(viewsets.ModelViewSet): serializer_class = TeamSerializer @@ -855,6 +851,8 @@ class TeamViewSet(viewsets.ModelViewSet): return Team.objects.filter(owner=self.request.user) def perform_create(self, serializer): + logger.info(f"Creating new team for user: {self.request.user.email}") + with transaction.atomic(): category = serializer.validated_data['category'] @@ -864,12 +862,12 @@ class TeamViewSet(viewsets.ModelViewSet): category.save() category.refresh_from_db() # F() 式の結果を評価 - serializer.save(owner=self.request.user, zekken_number=zekken_number) - - team = self.get_object() + team = serializer.save(owner=self.request.user, zekken_number=zekken_number) + logger.info(f"Team created successfully: {team.id}") # 外部システムの更新 - success = update_external_system( + success = self.register_team( + team.owner, team.zekken_number, team.owner.event_code, team.team_name, @@ -877,21 +875,93 @@ class TeamViewSet(viewsets.ModelViewSet): team.owner.password ) if not success: + logger.error("Failed to register external system") raise serializers.ValidationError("外部システムの更新に失敗しました。") + logger.info("External system register successfully") + def register_team(self, owner,zekken_number,event_code,team_name,category_name,password): + logger.info(f"register_team ==> zekken_number={zekken_number},event_code={event_code},team_name={team_name},category_name={category_name},password={password}") + api_url = f"{settings.FRONTEND_URL}/gifuroge/register_team" + user = owner + + headers = { + "Content-Type": "application/x-www-form-urlencoded" + } + data = { + "zekken_number": zekken_number, + "event_code": event_code, + "team_name": team_name, + "class_name": category_name, + "password": password # パスワードの扱いに注意が必要です + } + + try: + response = requests.post(api_url,headers=headers,data=data) + response.raise_for_status() + logger.info(f"Team registered successfully for team {team_name}") + return True + + except requests.RequestException as e: + logger.error(f"Failed to register team for entry {entry.id}. Error: {str(e)}") + # エラーが発生しても、エントリー自体は作成されています + # 必要に応じて、ここでエラーハンドリングを追加できます + return False def destroy(self, request, *args, **kwargs): team = self.get_object() if team.members.exists(): - return Response({"error": "チームにメンバーが残っているため削除できません。"}, status=status.HTTP_400_BAD_REQUEST) + return Response( + { + "error": "チームにメンバーが残っているため削除できません。", + "error_code": "TEAM_HAS_MEMBERS" + }, + status=status.HTTP_400_BAD_REQUEST + ) + + #return Response({"error": "チームにメンバーが残っているため削除できません。"}, status=status.HTTP_400_BAD_REQUEST) return super().destroy(request, *args, **kwargs) + def update_external_system(zekken_number, event_code, team_name, class_name, password): + api_url = f"{settings.FRONTEND_URL}/gifuroge/update_team_name" + headers = { + "Content-Type": "application/x-www-form-urlencoded" + } + data = { + "zekken_number": zekken_number, + "new_team_name": team_name, + "event_code": event_code, + } + try: + response = requests.post(api_url,headers=headers, data=data) + response.raise_for_status() + + return True + except requests.RequestException as e: + logger.error(f"Failed to update external system. Error: {str(e)}") + return False + def update(self, request, *args, **kwargs): try: - return super().update(request, *args, **kwargs) + partial = kwargs.pop('partial', False) + instance = self.get_object() + serializer = self.get_serializer(instance, data=request.data, partial=partial) + serializer.is_valid(raise_exception=True) + self.perform_update(serializer) + + if getattr(instance, '_prefetched_objects_cache', None): + instance._prefetched_objects_cache = {} + + return Response(serializer.data) except Exception as e: return Response({"error": "更新に失敗しました。競合が発生した可能性があります。"}, status=status.HTTP_409_CONFLICT) + +# def update(self, request, *args, **kwargs): +# try: +# return super().update(request, *args, **kwargs) +# except Exception as e: +# return Response({"error": "更新に失敗しました。競合が発生した可能性があります。"}, status=status.HTTP_409_CONFLICT) + def perform_update(self, serializer): with transaction.atomic(): team = serializer.save() @@ -906,7 +976,8 @@ class TeamViewSet(viewsets.ModelViewSet): ) if not success: raise serializers.ValidationError("外部システムの更新に失敗しました。") - + else: + print("岐阜ロゲシステム更新に成功") @action(detail=True, methods=['post']) def copy(self, request, pk=None): @@ -1004,27 +1075,6 @@ class EntryViewSet(viewsets.ModelViewSet): def perform_create(self, serializer): serializer.save(owner=self.request.user) - def register_team(self, entry): - api_url = f"{settings.FRONTEND_URL}/gifuroge/register_team" - user = self.request.user - - data = { - "zekken_number": entry.team.zekken_number, - "event_code": entry.event.event_code, - "team_name": entry.team.team_name, - "class_name": entry.category.category_name, - "password": user.password # パスワードの扱いに注意が必要です - } - - try: - response = requests.post(api_url, data=data) - response.raise_for_status() - logger.info(f"Team registered successfully for entry {entry.id}") - except requests.RequestException as e: - logger.error(f"Failed to register team for entry {entry.id}. Error: {str(e)}") - # エラーが発生しても、エントリー自体は作成されています - # 必要に応じて、ここでエラーハンドリングを追加できます - def update(self, request, *args, **kwargs): logger.info(f"Update method called for Entry with ID: {kwargs.get('pk')}") @@ -1074,7 +1124,7 @@ class EntryViewSet(viewsets.ModelViewSet): class MemberViewSet(viewsets.ModelViewSet): - #serializer_class = MemberSerializer + serializer_class = MemberSerializer permission_classes = [permissions.IsAuthenticated,IsTeamOwner] def get_serializer_class(self): @@ -1086,12 +1136,64 @@ class MemberViewSet(viewsets.ModelViewSet): team_id = self.kwargs['team_id'] return Member.objects.filter(team_id=team_id) + @action(detail=False, methods=['DELETE']) + def destroy_all(self, request, team_id=None): + team = Team.objects.get(id=team_id) + + # チームのオーナーかどうかを確認 + if team.owner != request.user: + return Response({"error": "チームのオーナーのみがメンバーを一括削除できます。"}, status=status.HTTP_403_FORBIDDEN) + + # 確認パラメータをチェック + confirm = request.query_params.get('confirm', '').lower() + if confirm != 'true': + return Response({"error": "確認パラメータが必要です。'?confirm=true'を追加してください。"}, status=status.HTTP_400_BAD_REQUEST) + + try: + with transaction.atomic(): + # オーナー以外のすべてのメンバーを削除 + #deleted_count = Member.objects.filter(team=team).exclude(user=team.owner).delete()[0] + deleted_count = Member.objects.filter(team=team).delete()[0] + + return Response({ + "message": f"{deleted_count}人のメンバーが削除されました。", + "deleted_count": deleted_count + }, status=status.HTTP_200_OK) + + except Exception as e: + return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + def destroy(self, request, *args, **kwargs): + instance = self.get_object() + team = instance.team + + # チームのオーナーかどうかを確認 + if team.owner != request.user: + return Response({"error": "チームのオーナーのみがメンバーを削除できます。"}, status=status.HTTP_403_FORBIDDEN) + + # オーナー自身は削除できないようにする + #if instance.user == team.owner: + # return Response({"error": "チームのオーナーは削除できません。"}, status=status.HTTP_400_BAD_REQUEST) + + self.perform_destroy(instance) + return Response(status=status.HTTP_204_NO_CONTENT) + def create(self, request, *args, **kwargs): + logger.info(f"Attempting to create new member for team: {self.kwargs['team_id']}") + logger.debug(f"Received data: {request.data}") + team = Team.objects.get(id=self.kwargs['team_id']) - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) + #serializer = self.get_serializer(data=request.data) + serializer = self.get_serializer(data=request.data, context={'team': team}) + + try: + serializer.is_valid(raise_exception=True) + except serializers.ValidationError as e: + logger.error(f"Validation error: {e.detail}") + return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST) email = serializer.validated_data.get('email', '') + logger.info(f"Processing member with email: {email}") user_data = { 'firstname': serializer.validated_data.get('firstname', ''), @@ -1100,40 +1202,64 @@ class MemberViewSet(viewsets.ModelViewSet): 'female': serializer.validated_data.get('female', False), } - if not email or email.startswith('dummy_'): - # 直接登録 - if email: - user, created = CustomUser.objects.get_or_create( - email=email, - defaults={**user_data, 'is_active':True} - ) + # 自分自身を登録する場合 + if request.user.email == email: + member, created = Member.objects.get_or_create(user=request.user, team=team) + if created: + return Response(MemberSerializer(member).data, status=status.HTTP_201_CREATED) else: - # emailが空の場合、一意のダミーメールアドレスを生成 - dummy_email = f"dummy_{uuid.uuid4()}@example.com" - user = CustomUser.objects.create(email=dummy_email, **user_data) + return Response({"message": "You are already a member of this team."}, status=status.HTTP_200_OK) - member = Member.objects.create(user=user, team=team) - return Response(MemberSerializer(member).data, status=status.HTTP_201_CREATED) + if not email or email.startswith('dummy_'): + logger.info("Processing as direct registration") + # 直接登録 + try: + if email: + user, created = CustomUser.objects.get_or_create( + email=email, + defaults={**user_data, 'is_active':True} + ) + else: + # emailが空の場合、一意のダミーメールアドレスを生成 + dummy_email = f"dummy_{uuid.uuid4()}@example.com" + user = CustomUser.objects.create(email=dummy_email, **user_data) + + member = Member.objects.create(user=user, team=team) + logger.info(f"Member created successfully: {member.id}") + return Response(MemberSerializer(member).data, status=status.HTTP_201_CREATED) + except Exception as e: + logger.error(f"Error creating member: {str(e)}") + return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST) #member = serializer.save(team=team) #return Response(serializer.data, status=status.HTTP_201_CREATED) else: + logger.info("Processing as temporary registration") + # 仮登録 existing_user = CustomUser.objects.filter(email=email).first() if existing_user: + logger.info(f"Existing user found: {existing_user.id}") + # 既存ユーザーの場合、チーム招待メールを送信 - send_team_join_email(existing_user, team) + send_team_join_email(request.user,existing_user, team) return Response({"message": "Invitation for your team sent to existing user."}, status=status.HTTP_200_OK) else: - print("新規ユーザー") - temp_user = TempUser.objects.create( - email=email, - **user_data, - verification_code=str(uuid.uuid4()) - ) - send_invitation_email(temp_user, team) #仮登録済みでも確認メールを送る。 + logger.info("Inviting new temporary user") - return Response({"message": "Invitation email sent to the user."}, status=status.HTTP_201_CREATED) + try: + print("新規ユーザー") + #temp_user = TempUser.objects.create( + # email=email, + # **user_data, + # verification_code=str(uuid.uuid4()) + #) + send_invitation_email(request.user,request,email, team) #仮登録済みでも確認メールを送る。 + logger.info(f"Invitation email sent to: {email}") + return Response({"message": "Invitation email sent to the user."}, status=status.HTTP_201_CREATED) + except Exception as e: + logger.info(f"Error on Invitation email sent to: {email} : {e}") + return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST) ''' @@ -1252,6 +1378,59 @@ class MemberViewSet(viewsets.ModelViewSet): self.check_object_permissions(self.request, obj) return obj +class ActivateMemberView(APIView): + def get(self, request, user_id, team_id): + user = get_object_or_404(CustomUser, id=user_id) + team = get_object_or_404(Team, id=team_id) + + # メンバーオブジェクトを取得または作成 + member, created = Member.objects.get_or_create(user=user, team=team) + + if member.lastname == "dummy": + # userデータでmemberデータを更新 + member.lastname = user.lastname + member.firstname = user.firstname + member.date_of_birth = user.date_of_birth + member.female = user.female + member.save() + + message = f"{team.team_name}に正常に参加し、メンバー情報が更新されました。アプリのチーム管理で確認できます。" + else: + message = f"{team.team_name}に正常に参加しました。" + + return render(request, 'activation_success.html', {'message': message}) + + +class ActivateNewMemberView(APIView): + def get(self, request, verification_code, team_id): + try: + team = Team.objects.get(id=team_id) + member, created = Member.objects.get_or_create(team=team, user__email=email) + if created: + return Response({'message': 'Member activated successfully'}, status=status.HTTP_201_CREATED) + else: + return Response({'message': 'Member already exists'}, status=status.HTTP_200_OK) + except Team.DoesNotExist: + return Response({'message': 'Invalid team'}, status=status.HTTP_400_BAD_REQUEST) + +''' + temp_user = get_object_or_404(TempUser, verification_code=verification_code) + team = get_object_or_404(Team, id=team_id) + + user = CustomUser.objects.create_user( + email=temp_user.email, + password=temp_user.password, # Ensure this is securely handled + **{k: v for k, v in temp_user.__dict__.items() if k in ['firstname', 'lastname', 'date_of_birth', 'female']} + ) + + Member.objects.create(user=user, team=team) + temp_user.delete() + + message = f"アカウントが作成され、{team.team_name}に参加しました。アプリのチーム管理で確認できます。" + return render(request, 'activation_success.html', {'message': message}) +''' + + class OwnerEntriesView(generics.ListAPIView): serializer_class = EntrySerializer permission_classes = [permissions.IsAuthenticated] @@ -1461,13 +1640,24 @@ class VerifyEmailView(APIView): password=temp_user.password, **{k: v for k, v in user_data.items() if k != 'email'} ) - temp_user.delete() - return Response({'message': 'Email verified and user created'}, status=status.HTTP_201_CREATED) + # チームへの追加処理(もし必要なら) + if hasattr(temp_user, 'team_id'): + team = Team.objects.get(id=temp_user.team_id) + Member.objects.create(user=user, team=team) + message = f"メールアドレスが確認され、アカウントが作成されました。{team.team_name}のメンバーとして登録されています。アプリでログインして、必要な情報を入力してください。" + else: + message = "メールアドレスが確認され、アカウントが作成されました。アプリでログインして、必要な情報を入力してください。" + + temp_user.delete() + return render(request, 'verification_success.html', {'message': message}) else: - return Response({'message': 'Verification link expired'}, status=status.HTTP_400_BAD_REQUEST) + message = "確認リンクの有効期限が切れています。アプリから認証コードの再送信をしてください。" + return render(request, 'verification_success.html', {'message': message}) + except TempUser.DoesNotExist: - return Response({'message': 'Invalid verification code'}, status=status.HTTP_400_BAD_REQUEST) + message = "無効な確認コードです。" + return render(request, 'verification_success.html', {'message': message}) class MemberUserDetailView(generics.RetrieveAPIView):