diff --git a/config/settings.py b/config/settings.py
index a26b01c..69991cf 100644
--- a/config/settings.py
+++ b/config/settings.py
@@ -252,3 +252,5 @@ PASSWORD_HASHERS = [
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
]
+BLACKLISTED_IPS = ['44.230.58.114'] # ブロックしたい IP アドレスをここにリストとして追加
+
diff --git a/rog/admin.py b/rog/admin.py
index bcfb85b..9274d55 100644
--- a/rog/admin.py
+++ b/rog/admin.py
@@ -6,11 +6,16 @@ from leaflet.admin import LeafletGeoAdminMixin
from leaflet_admin_list.admin import LeafletAdminListMixin
from .models import RogUser, Location, SystemSettings, JoinedEvent, Favorite, TravelList, TravelPoint, ShapeLayers, Event, Location_line, Location_polygon, JpnAdminMainPerf, Useractions, CustomUser, GifuAreas, UserTracks, templocation, UserUpload, EventUser, GoalImages, CheckinImages, NewEvent, NewEvent2, Team, NewCategory, Category, Entry, Member, TempUser
from django.contrib.auth.admin import UserAdmin
-from django.urls import path
+from django.urls import path,reverse
from django.shortcuts import render
from django import forms;
import requests
+from django.http import HttpResponseRedirect
+from django.utils.html import format_html
+from .forms import CSVUploadForm
+from .views import process_csv_upload
+
class RogAdmin(LeafletAdminListMixin, LeafletGeoAdminMixin, admin.ModelAdmin):
list_display=['title', 'venue', 'at_date',]
@@ -196,10 +201,42 @@ class TempLocationAdmin(LeafletGeoAdmin):
list_display = ('location_id','cp', 'location_name', 'category', 'event_name', 'event_active', 'auto_checkin', 'checkin_radius', 'checkin_point', 'buy_point',)
actions = [tranfer_to_location,]
+
@admin.register(NewEvent2)
class NewEvent2Admin(admin.ModelAdmin):
- list_display = ['event_name', 'start_datetime', 'end_datetime']
- search_fields = ['event_name']
+ list_display = ['event_name', 'start_datetime', 'end_datetime', 'csv_upload_button']
+
+ def get_urls(self):
+ urls = super().get_urls()
+ my_urls = [
+ path('csv-upload/', self.admin_site.admin_view(self.csv_upload_view), name='newevent2_csv_upload'),
+ ]
+ return my_urls + urls
+
+ def csv_upload_view(self, request):
+ if request.method == 'POST':
+ form = CSVUploadForm(request.POST, request.FILES)
+ if form.is_valid():
+ csv_file = request.FILES['csv_file']
+ event = form.cleaned_data['event']
+ process_csv_upload(csv_file, event)
+ self.message_user(request, "CSV file has been processed successfully.")
+ return HttpResponseRedirect("../")
+ else:
+ form = CSVUploadForm()
+
+ return render(request, 'admin/csv_upload.html', {'form': form})
+
+ def csv_upload_button(self, obj):
+ url = reverse('admin:newevent2_csv_upload')
+ return format_html('CSVアップロード', url)
+ csv_upload_button.short_description = 'CSV Upload'
+
+ def changelist_view(self, request, extra_context=None):
+ extra_context = extra_context or {}
+ extra_context['csv_upload_url'] = reverse('admin:newevent2_csv_upload')
+ return super().changelist_view(request, extra_context=extra_context)
+
@admin.register(Team)
class TeamAdmin(admin.ModelAdmin):
diff --git a/rog/forms.py b/rog/forms.py
new file mode 100644
index 0000000..347bc66
--- /dev/null
+++ b/rog/forms.py
@@ -0,0 +1,7 @@
+from django import forms
+from .models import NewEvent2
+
+class CSVUploadForm(forms.Form):
+ event = forms.ModelChoiceField(queryset=NewEvent2.objects.all(), label="イベント選択")
+ csv_file = forms.FileField(label="CSVファイル")
+
diff --git a/rog/serializers.py b/rog/serializers.py
index ff22e65..fb96b62 100644
--- a/rog/serializers.py
+++ b/rog/serializers.py
@@ -489,6 +489,17 @@ class EntrySerializer(serializers.ModelSerializer):
event = data.get('event')
category = data.get('category')
entry_date = data.get('date')
+ if isinstance(entry_date, datetime):
+ entry_date = entry_date.date()
+ elif isinstance(entry_date, str):
+ entry_date = datetime.strptime(entry_date, "%Y-%m-%d").date()
+
+ logger.debug(f"==== entry_date : {entry_date} ====")
+
+ if entry_date < event.start_datetime.date() or entry_date > event.end_datetime.date():
+ raise serializers.ValidationError(f"日付は{event.start_datetime.date()}から{event.end_datetime.date()}の間である必要があります。")
+
+
owner = self.context['request'].user
zekken_number = data.get('zekken_number')
@@ -534,6 +545,11 @@ class EntrySerializer(serializers.ModelSerializer):
ret['event'] = NewEvent2Serializer(instance.event).data
ret['category'] = NewCategorySerializer(instance.category).data
ret['owner'] = CustomUserSerializer(instance.owner).data
+
+ if isinstance(ret['date'], datetime):
+ ret['date'] = ret['date'].date().isoformat()
+ elif isinstance(ret['date'], date):
+ ret['date'] = ret['date'].isoformat()
return ret
#if isinstance(ret['date'], datetime):
diff --git a/rog/templates/admin/csv_upload.html b/rog/templates/admin/csv_upload.html
new file mode 100644
index 0000000..9349cb4
--- /dev/null
+++ b/rog/templates/admin/csv_upload.html
@@ -0,0 +1,24 @@
+{% extends "admin/base_site.html" %}
+{% load i18n admin_urls %}
+
+{% block content %}
+
+{% endblock %}
diff --git a/rog/urls.py b/rog/urls.py
index ee5c2c6..e75a6ef 100644
--- a/rog/urls.py
+++ b/rog/urls.py
@@ -9,7 +9,13 @@ from .views import TestActionViewSet
from .views import OwnerEntriesView, OwnerTeamsView, OwnerMembersView
+from django.urls import path
+from . import views
+#from .views import NewEvent2AdminView
+
+
router = DefaultRouter()
+router.register(r'newevent2', views.NewEvent2ViewSet)
router.register(prefix='location', viewset=LocationViewSet, basename='location')
router.register(prefix='location_line', viewset=Location_lineViewSet, basename="location_line")
@@ -37,6 +43,7 @@ router.register(r'members', MemberViewSet, basename='member')
router.register(r'teams/(?P\d+)/members', MemberViewSet, basename='team-members')
router.register(r'categories-viewset', NewCategoryViewSet)
+router.register(r'newevent2', views.NewEvent2ViewSet)
# Akira 追加
# GET /api/members//user/: 特定のメンバーのユーザー情報を取得
@@ -96,5 +103,9 @@ urlpatterns += [
path('reset-password///', PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
path('users//last-goal/', UserLastGoalTimeView.as_view(), name='user-last-goal-time'),
path('teams//entries/', TeamEntriesView.as_view(), name='team-entries'),
+ #path('admin/newevent2/', NewEvent2AdminView.as_view(), name='newevent2-admin'),
+ path('newevent2-list/', views.NewEvent2ListView.as_view(), name='newevent2-list'),
+ #path('admin/newevent2/csv-upload/', NewEvent2Admin.as_view({'get': 'csv_upload_view', 'post': 'csv_upload_view'}), name='rog_newevent2_csv-upload'),
+ #path('admin/', admin.site.urls),
]
diff --git a/rog/views.py b/rog/views.py
index f0cf73e..32d5c0b 100644
--- a/rog/views.py
+++ b/rog/views.py
@@ -70,9 +70,127 @@ from django.contrib.gis.geos import Point
from django.contrib.gis.db.models.functions import Distance
from django.utils.dateparse import parse_date
+from django.utils import timezone
+
+import csv
+import io
+from django.contrib import admin
+from django.shortcuts import render
+from django.http import HttpResponseRedirect
+from django.urls import path
+from django import forms
+from .models import NewEvent2, CustomUser, Team, NewCategory, Entry, Member, TempUser
+from django.core.mail import send_mail
+from django.conf import settings
+from django.db import transaction
+from django.core.exceptions import ValidationError
logger = logging.getLogger(__name__)
+def process_csv_upload(csv_file, event):
+ decoded_file = csv_file.read().decode('utf-8')
+ io_string = io.StringIO(decoded_file)
+ reader = csv.reader(io_string)
+ next(reader) # ヘッダーをスキップ
+
+ for row in reader:
+ try:
+ owner_email, owner_name, owner_grade, owner_gender, team_name, category_name, *member_data = row
+ # ここでデータを処理し、必要なオブジェクトを作成します
+ # 例: create_or_update_team(owner_email, owner_name, owner_grade, owner_gender, team_name, category_name, event)
+ # 例: process_members(member_data, team)
+ except ValidationError as e:
+ # エラーハンドリング
+ pass
+
+
+class CSVUploadForm(forms.Form):
+ event = forms.ModelChoiceField(queryset=NewEvent2.objects.all())
+ csv_file = forms.FileField()
+
+class NewEvent2Admin(admin.ModelAdmin):
+ list_display = ['event_name', 'start_datetime', 'end_datetime']
+
+ def get_urls(self):
+ urls = super().get_urls()
+ my_urls = [
+ path('csv-upload/', self.admin_site.admin_view(self.csv_upload_view), name='csv-upload'),
+ ]
+ return my_urls + urls
+
+ def csv_upload_view(self, request):
+ if request.method == 'POST':
+ form = CSVUploadForm(request.POST, request.FILES)
+ if form.is_valid():
+ event = form.cleaned_data['event']
+ csv_file = request.FILES['csv_file']
+ csv_data = csv_file.read().decode('utf-8')
+ csv_reader = csv.reader(io.StringIO(csv_data))
+ next(csv_reader) # ヘッダーをスキップ
+
+ for row in csv_reader:
+ self.process_team(event, row)
+
+ self.message_user(request, "CSV file has been processed successfully.")
+ return HttpResponseRedirect("..")
+ else:
+ form = CSVUploadForm()
+
+ context = {
+ 'form': form,
+ 'title': 'Upload CSV file',
+ }
+ return render(request, 'admin/csv_upload.html', context)
+
+ @transaction.atomic
+ def process_team(self, event, row):
+ owner_data = row[:4]
+ team_name = row[4]
+ category_name = row[5]
+ members_data = row[6:]
+
+ owner = self.create_or_get_user(*owner_data)
+ category = NewCategory.objects.get(category_name=category_name)
+ team, _ = Team.objects.get_or_create(owner=owner, team_name=team_name, category=category)
+
+ entry, _ = Entry.objects.get_or_create(
+ team=team,
+ event=event,
+ category=category,
+ owner=owner,
+ date=event.start_datetime.date()
+ )
+
+ for i in range(0, len(members_data), 4):
+ member_data = members_data[i:i+4]
+ if member_data[0]: # メールアドレスがある場合のみ処理
+ member = self.create_or_get_user(*member_data)
+ Member.objects.get_or_create(team=team, user=member)
+
+ def create_or_get_user(self, email, name, grade, gender):
+ is_dummy = False
+ if not email:
+ email = f"dummy_{uuid.uuid4()}@example.com"
+ is_dummy = True
+
+ user, created = CustomUser.objects.get_or_create(email=email)
+ if created or is_dummy:
+ user.firstname, user.lastname = name.split(' ', 1) if ' ' in name else (name, '')
+ user.grade = grade
+ user.gender = gender
+ user.is_active = not is_dummy
+ user.save()
+
+ if not is_dummy:
+ self.send_invitation_email(user)
+
+ return user
+
+ def send_invitation_email(self, user):
+ subject = 'Invitation to join the event'
+ message = f'Please click the following link to activate your account: {settings.SITE_URL}/activate/{user.id}/'
+ send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [user.email])
+
class LocationViewSet(viewsets.ModelViewSet):
queryset=Location.objects.all()
@@ -1060,12 +1178,12 @@ class RegistrationView(APIView):
class NewEvent2ViewSet(viewsets.ModelViewSet):
queryset = NewEvent2.objects.all()
serializer_class = NewEvent2Serializer
- permission_classes = [IsAuthenticated]
+ permission_classes = [permissions.IsAuthenticated]
class NewEvent2ListView(generics.ListAPIView):
queryset = NewEvent2.objects.all()
serializer_class = NewEvent2Serializer
- permission_classes = [IsAuthenticated]
+ permission_classes = [permissions.IsAuthenticated]
class NewEventViewSet(viewsets.ModelViewSet):
queryset = NewEvent.objects.all()
@@ -1370,6 +1488,15 @@ class EntryViewSet(viewsets.ModelViewSet):
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
+
+ # イベントの締め切り日時をチェック
+ event = serializer.validated_data['event']
+ if event.deadlineDateTime and timezone.now() > event.deadlineDateTime:
+ return Response(
+ {"error": "締め切りを過ぎているため、エントリーを作成できません。"},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
try:
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)