updated for team, member and email registration

This commit is contained in:
2024-07-20 14:24:52 +09:00
parent 6de4aabebb
commit 1a8e97e945
5 changed files with 340 additions and 14 deletions

View File

@ -0,0 +1,16 @@
from django.core.management.base import BaseCommand
from django.utils import timezone
from rog.models import TempUser # アプリ名 'rog' を適切に変更してください
class Command(BaseCommand):
help = 'Deletes expired temporary user records'
def handle(self, *args, **options):
expired_users = TempUser.objects.filter(expires_at__lt=timezone.now())
count = expired_users.count()
expired_users.delete()
self.stdout.write(self.style.SUCCESS(f'Successfully deleted {count} expired temporary user records'))
# cron job の設定
# 0 3 * * * /path/to/your/python /path/to/your/manage.py cleanup_temp_users

View File

@ -24,6 +24,12 @@ from django.db import transaction
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
from django.contrib.postgres.indexes import GistIndex from django.contrib.postgres.indexes import GistIndex
from django.db import models
from django.contrib.auth.hashers import make_password
from django.utils import timezone
from datetime import timedelta
import csv import csv
import codecs import codecs
import sys import sys
@ -61,20 +67,108 @@ def remove_bom_inplace(path):
fp.seek(-bom_length, os.SEEK_CUR) fp.seek(-bom_length, os.SEEK_CUR)
fp.truncate() fp.truncate()
#========== Akira ここから
class TempUser(models.Model):
email = models.EmailField(unique=True)
password = models.CharField(max_length=128)
is_rogaining = models.BooleanField(default=False)
zekken_number = models.CharField(max_length=255, blank=True, null=True)
event_code = models.CharField(max_length=255, blank=True, null=True)
team_name = models.CharField(max_length=255, blank=True, null=True)
group = models.CharField(max_length=255)
verification_code = models.UUIDField(default=uuid.uuid4, editable=False)
created_at = models.DateTimeField(auto_now_add=True)
expires_at = models.DateTimeField()
def __str__(self):
return self.email
def save(self, *args, **kwargs):
if not self.expires_at:
self.expires_at = timezone.now() + timedelta(hours=24) # 24時間の有効期限
super().save(*args, **kwargs)
def is_valid(self):
return timezone.now() <= self.expires_at
class Team(models.Model):
zekken_number = models.CharField(max_length=255, primary_key=True)
team_name = models.CharField(max_length=255)
password = models.CharField(max_length=128)
def __str__(self):
return f"{self.zekken_number} - {self.team_name}"
class Member(models.Model):
zekken_number = models.ForeignKey(Team, on_delete=models.CASCADE)
userid = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
class Meta:
unique_together = ('zekken_number', 'userid')
def __str__(self):
return f"{self.zekken_number} - {self.userid}"
class Entry(models.Model):
zekken_number = models.ForeignKey(Team, on_delete=models.CASCADE)
event_code = models.CharField(max_length=255)
date = models.DateField()
class Meta:
unique_together = ('zekken_number', 'event_code', 'date')
def __str__(self):
return f"{self.zekken_number} - {self.event_code} - {self.date}"
#============= Akira ここまで
class CustomUserManager(BaseUserManager): class CustomUserManager(BaseUserManager):
def create_user(self, email, password, group, event_code, team_name, **other_fields): def create_user(self, email, password, group, event_code, team_name, **other_fields):
if not email: if not email:
raise ValueError(_("You must provide an email address")) raise ValueError(_("You must provide an email address"))
user = self.model(email=email, group=group, event_code=event_code, team_name=team_name, zekken_number=email, is_rogaining=True, **other_fields) # ユニークなuseridを生成
userid = str(uuid.uuid4())
user = self.model(
email=self.normalize_email(email),
firstname=firstname,
lastname=lastname,
userid=userid,
date_of_birth=date_of_birth,
)
user.set_password(password) user.set_password(password)
user.save() user.save()
return user return user
def create_superuser(self, email, password, group, event_code=None, team_name=None, **other_fields): def create_superuser(self, email, password, group, event_code=None, team_name=None, **other_fields):
user = self.create_user(
email,
firstname=firstname,
lastname=lastname,
date_of_birth=date_of_birth,
password=password,
)
user.is_staff = True
user.is_superuser = True
user.is_active = True
user.save(using=self._db)
return user
/*
# Providing default values for event_code and team_name if they are not provided # Providing default values for event_code and team_name if they are not provided
if event_code is None: if event_code is None:
event_code = 'test' # replace this with some default value event_code = 'test' # replace this with some default value
@ -91,8 +185,27 @@ class CustomUserManager(BaseUserManager):
raise ValueError(_('Superuser must have is_superuser set to True')) raise ValueError(_('Superuser must have is_superuser set to True'))
return self.create_user(email, password, group, event_code, team_name, **other_fields) return self.create_user(email, password, group, event_code, team_name, **other_fields)
*/
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
firstname = models.CharField(max_length=255)
lastname = models.CharField(max_length=255)
userid = models.CharField(max_length=255, unique=True)
date_of_birth = models.DateField()
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
event_code = models.CharField(max_length=255, blank=True, null=True)
team_name = models.CharField(max_length=255, blank=True, null=True)
zekken_number = models.CharField(max_length=255, blank=True, null=True)
objects = CustomUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['firstname', 'lastname', 'userid', 'date_of_birth']
def __str__(self):
return self.email
class JpnAdminMainPerf(models.Model): class JpnAdminMainPerf(models.Model):
geom = models.MultiPolygonField(blank=True, null=True) geom = models.MultiPolygonField(blank=True, null=True)
@ -197,7 +310,7 @@ class UserUploadUser(models.Model):
email=models.CharField(_('User Email'), max_length=255) email=models.CharField(_('User Email'), max_length=255)
class CustomUser(AbstractBaseUser, PermissionsMixin): class CustomUser_old(AbstractBaseUser, PermissionsMixin):
class Groups(models.TextChoices): class Groups(models.TextChoices):
GB1 = '大垣-初心者', '大垣-初心者' GB1 = '大垣-初心者', '大垣-初心者'
GB2 = '大垣-3時間', '大垣-3時間' GB2 = '大垣-3時間', '大垣-3時間'

View File

@ -1,14 +1,16 @@
from rest_framework import serializers from rest_framework import serializers
from rest_framework_gis.serializers import GeoFeatureModelSerializer from rest_framework_gis.serializers import GeoFeatureModelSerializer
from sqlalchemy.sql.functions import mode from sqlalchemy.sql.functions import mode
from .models import Location, Location_line, Location_polygon, JpnAdminMainPerf, Useractions, GifuAreas, RogUser, UserTracks, GoalImages, CheckinImages,CustomUser from .models import Location, Location_line, Location_polygon, JpnAdminMainPerf, Useractions, GifuAreas, RogUser, UserTracks, GoalImages, CheckinImages,CustomUser, Team, Member, Entry
from drf_extra_fields.fields import Base64ImageField from drf_extra_fields.fields import Base64ImageField
from django.contrib.auth.hashers import make_password
#from django.contrib.auth.models import User #from django.contrib.auth.models import User
from .models import CustomUser from .models import CustomUser
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from .models import TestModel from .models import TestModel
from .models import TempUser
class LocationCatSerializer(serializers.ModelSerializer): class LocationCatSerializer(serializers.ModelSerializer):
@ -53,6 +55,39 @@ class JPN_main_perfSerializer(serializers.ModelSerializer):
# model=JpnAdminPerf # model=JpnAdminPerf
# fields=['id','et_id', 'et_right', 'et_left', 'adm2_l', 'adm1_l', 'adm0_l', 'adm0_r', 'adm1_r', 'adm2_r', 'admlevel'] # fields=['id','et_id', 'et_right', 'et_left', 'adm2_l', 'adm1_l', 'adm0_l', 'adm0_r', 'adm1_r', 'adm2_r', 'admlevel']
#============= Akira ここから
class TeamSerializer(serializers.ModelSerializer):
class Meta:
model = Team
fields = ['zekken_number', 'team_name', 'password']
extra_kwargs = {'password': {'write_only': True}}
class MemberSerializer(serializers.ModelSerializer):
class Meta:
model = Member
fields = ['zekken_number', 'userid']
class EntrySerializer(serializers.ModelSerializer):
class Meta:
model = Entry
fields = ['zekken_number', 'event_code', 'date']
class CustomUserSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ['email', 'firstname', 'lastname', 'userid', 'date_of_birth', 'password']
extra_kwargs = {
'password': {'write_only': True},
'userid': {'read_only': True}
}
def create(self, validated_data):
validated_data['password'] = make_password(validated_data.get('password'))
return super(CustomUserSerializer, self).create(validated_data)
#============= Akira ここまで
class GifuAreaSerializer(serializers.ModelSerializer): class GifuAreaSerializer(serializers.ModelSerializer):
class Meta: class Meta:
@ -192,4 +227,4 @@ class RegistrationSerializer(serializers.ModelSerializer):
raise serializers.ValidationError({'password': 'Passwords must match.'}) raise serializers.ValidationError({'password': 'Passwords must match.'})
user.set_password(password) user.set_password(password)
user.save() user.save()
return user return user

View File

@ -1,7 +1,8 @@
from sys import prefix from sys import prefix
from rest_framework import urlpatterns from rest_framework import urlpatterns
from rest_framework.routers import DefaultRouter 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 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, VerifyEmailView, TeamViewSet, MemberViewSet, EntryViewSet
from django.urls import path, include from django.urls import path, include
from knox import views as knox_views from knox import views as knox_views
@ -18,6 +19,17 @@ router.register(prefix='track', viewset=UserTracksViewSet, basename='track')
router.register(prefix='goalimage', viewset=GoalImageViewSet, basename='goalimage') router.register(prefix='goalimage', viewset=GoalImageViewSet, basename='goalimage')
router.register(prefix='checkinimage', viewset=CheckinImageViewSet, basename='checkinimage') router.register(prefix='checkinimage', viewset=CheckinImageViewSet, basename='checkinimage')
#Akira 追加
# /api/teams/ - チームの一覧取得と作成
# /api/teams/<pk>/ - 特定のチームの取得、更新、削除
# /api/members/ - メンバーの一覧取得と作成
# /api/members/<pk>/ - 特定のメンバーの取得、更新、削除
# /api/entries/ - エントリーの一覧取得と作成
# /api/entries/<pk>/ - 特定のエントリーの取得、更新、削除
#
router.register(r'teams', TeamViewSet)
router.register(r'members', MemberViewSet)
router.register(r'entries', EntryViewSet)
urlpatterns = router.urls urlpatterns = router.urls
@ -48,5 +60,7 @@ urlpatterns += [
path('delete-account/', DeleteAccount, name="delete-account"), path('delete-account/', DeleteAccount, name="delete-account"),
path('privacy/', PrivacyView, name='privacy-view'), path('privacy/', PrivacyView, name='privacy-view'),
path('register', RegistrationView.as_view(), name='register'), path('register', RegistrationView.as_view(), name='register'),
path('verify-email/<uuid:verification_code>/', VerifyEmailView.as_view(), name='verify_email'),
# path('goal-image/', GoalImageViewSet.as_view(), name='goal-image') # path('goal-image/', GoalImageViewSet.as_view(), name='goal-image')
] ]

View File

@ -1,15 +1,16 @@
from curses.ascii import NUL from curses.ascii import NUL
from django.core.serializers import serialize from django.core.serializers import serialize
from .models import GoalImages, Location, Location_line, Location_polygon, JpnAdminMainPerf, Useractions, GifuAreas, RogUser, CustomUser, UserTracks, GoalImages, CheckinImages from .models import GoalImages, Location, Location_line, Location_polygon, JpnAdminMainPerf, Useractions, GifuAreas, RogUser, CustomUser, UserTracks, GoalImages, CheckinImages, TempUser
from rest_framework import viewsets from rest_framework import viewsets,status
from .serializers import LocationSerializer, Location_lineSerializer, Location_polygonSerializer, JPN_main_perfSerializer, LocationCatSerializer, CreateUserSerializer, UserSerializer, LoginUserSerializer, UseractionsSerializer, UserDestinationSerializer, GifuAreaSerializer, LocationEventNameSerializer, RogUserSerializer, UserTracksSerializer, ChangePasswordSerializer, GolaImageSerializer, CheckinImageSerializer, RegistrationSerializer from .serializers import LocationSerializer, Location_lineSerializer, Location_polygonSerializer, JPN_main_perfSerializer, LocationCatSerializer, CreateUserSerializer, UserSerializer, LoginUserSerializer, UseractionsSerializer, UserDestinationSerializer, GifuAreaSerializer, LocationEventNameSerializer, RogUserSerializer, UserTracksSerializer, ChangePasswordSerializer, GolaImageSerializer, CheckinImageSerializer, RegistrationSerializer, Team, Member, Entry, CustomUserSerializer
from knox.models import AuthToken from knox.models import AuthToken
from rest_framework import viewsets, generics, status from rest_framework import viewsets, generics, status
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.parsers import JSONParser, MultiPartParser from rest_framework.parsers import JSONParser, MultiPartParser
from .serializers import LocationSerializer from .serializers import LocationSerializer, TeamSerializer, MemberSerializer, EntrySerializer
from django.http import JsonResponse from django.http import JsonResponse
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from django.contrib.gis.db.models import Extent, Union from django.contrib.gis.db.models import Extent, Union
@ -29,8 +30,9 @@ from rest_framework.parsers import JSONParser, MultiPartParser
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
import uuid import uuid
from django.shortcuts import render from django.shortcuts import render
from django.utils import timezone
from django.db import transaction
class LocationViewSet(viewsets.ModelViewSet): class LocationViewSet(viewsets.ModelViewSet):
queryset=Location.objects.all() queryset=Location.objects.all()
@ -53,6 +55,113 @@ class Jpn_Main_PerfViewSet(viewsets.ModelViewSet):
serializer_class=JPN_main_perfSerializer serializer_class=JPN_main_perfSerializer
filter_fields = ["adm1_ja"] filter_fields = ["adm1_ja"]
#===== AKira ここから
class CustomUserViewSet(viewsets.ModelViewSet):
queryset = CustomUser.objects.all()
serializer_class = CustomUserSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
return CustomUser.objects.filter(id=self.request.user.id)
class TeamViewSet(viewsets.ModelViewSet):
queryset = Team.objects.all()
serializer_class = TeamSerializer
@transaction.atomic
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
# チーム登録後にエントリー登録と外部APIコールを行う
self.register_entry_and_call_external_api(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def register_entry_and_call_external_api(self, team_data):
# エントリーの登録
entry_data = {
'zekken_number': team_data['zekken_number'],
'event_code': request.data.get('event_code'), # エントリー用のevent_codeを取得
'date': request.data.get('date') # エントリー用の日付を取得
}
entry_serializer = EntrySerializer(data=entry_data)
if entry_serializer.is_valid():
entry_serializer.save()
# 外部APIへのコール
self.call_external_api(team_data, entry_data)
else:
# エントリー登録に失敗した場合のエラーハンドリング
raise serializers.ValidationError(entry_serializer.errors)
def call_external_api(self, team_data, entry_data):
external_api_url = "https://rogaining.sumasen.net/gifuroge/register_team"
payload = {
'zekken_number': team_data['zekken_number'],
'event_code': entry_data['event_code'],
'team_name': team_data['team_name'],
'class_name': team_data.get('class_name', 'Default'), # class_nameがない場合はデフォルト値を設定
'password': team_data['password']
}
try:
response = requests.post(external_api_url, data=payload)
response.raise_for_status() # エラーレスポンスの場合は例外を発生させる
# レスポンスの処理(必要に応じて)
print(f"External API response: {response.json()}")
except requests.RequestException as e:
# 外部APIコールに失敗した場合のエラーハンドリング
print(f"Failed to call external API: {str(e)}")
# ここでエラーをログに記録したり、管理者に通知したりすることができます
def get_queryset(self):
user = self.request.user
return Team.objects.filter(member__userid=user)
class MemberViewSet(viewsets.ModelViewSet):
queryset = Member.objects.all()
serializer_class = MemberSerializer
permission_classes = [IsAuthenticated]
def create(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def get_queryset(self):
user = self.request.user
return Member.objects.filter(userid=user)
class EntryViewSet(viewsets.ModelViewSet):
queryset = Entry.objects.all()
serializer_class = EntrySerializer
permission_classes = [IsAuthenticated]
def create(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def get_queryset(self):
user = self.request.user
return Entry.objects.filter(zekken_number__member__userid=user)
#===== AKira ここまで
class UserTracksViewSet(viewsets.ModelViewSet): class UserTracksViewSet(viewsets.ModelViewSet):
@ -550,10 +659,49 @@ class TestActionViewSet(viewsets.ModelViewSet):
def PrivacyView(request): def PrivacyView(request):
return render(request, "rog/privacy.html") return render(request, "rog/privacy.html")
class RegistrationView(APIView): class RegistrationView_old(APIView):
def post(self, request): def post(self, request):
serializer = RegistrationSerializer(data=request.data) serializer = RegistrationSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
serializer.save() serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class RegistrationView(APIView):
def post(self, request):
serializer = RegistrationSerializer(data=request.data)
if serializer.is_valid():
temp_user = serializer.save()
verification_url = request.build_absolute_uri(
reverse('verify_email', kwargs={'verification_code': temp_user.verification_code})
)
send_mail(
'Verify your email',
f'Please click the link to verify your email: {verification_url}',
settings.DEFAULT_FROM_EMAIL,
[temp_user.email],
fail_silently=False,
)
return Response({"message": "Please check your email to complete registration."}, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class VerifyEmailView(APIView):
def get(self, request, verification_code):
try:
temp_user = TempUser.objects.get(verification_code=verification_code)
if not temp_user.is_valid():
temp_user.delete()
return Response({"error": "Verification link has expired. Please register again."}, status=status.HTTP_400_BAD_REQUEST)
user = CustomUser.objects.create_user(
email=temp_user.email,
password=temp_user.password,
is_rogaining=temp_user.is_rogaining,
zekken_number=temp_user.zekken_number,
event_code=temp_user.event_code,
team_name=temp_user.team_name,
group=temp_user.group
)