pre release 20240903

This commit is contained in:
hayano
2024-09-03 13:19:30 +00:00
parent 80ccaace3d
commit 6f0d8d15fd
7 changed files with 229 additions and 5 deletions

View File

@ -252,3 +252,5 @@ PASSWORD_HASHERS = [
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
] ]
BLACKLISTED_IPS = ['44.230.58.114'] # ブロックしたい IP アドレスをここにリストとして追加

View File

@ -6,11 +6,16 @@ from leaflet.admin import LeafletGeoAdminMixin
from leaflet_admin_list.admin import LeafletAdminListMixin 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 .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.contrib.auth.admin import UserAdmin
from django.urls import path from django.urls import path,reverse
from django.shortcuts import render from django.shortcuts import render
from django import forms; from django import forms;
import requests 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): class RogAdmin(LeafletAdminListMixin, LeafletGeoAdminMixin, admin.ModelAdmin):
list_display=['title', 'venue', 'at_date',] 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',) list_display = ('location_id','cp', 'location_name', 'category', 'event_name', 'event_active', 'auto_checkin', 'checkin_radius', 'checkin_point', 'buy_point',)
actions = [tranfer_to_location,] actions = [tranfer_to_location,]
@admin.register(NewEvent2) @admin.register(NewEvent2)
class NewEvent2Admin(admin.ModelAdmin): class NewEvent2Admin(admin.ModelAdmin):
list_display = ['event_name', 'start_datetime', 'end_datetime'] list_display = ['event_name', 'start_datetime', 'end_datetime', 'csv_upload_button']
search_fields = ['event_name']
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('<a class="button" href="{}">CSVアップロード</a>', 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) @admin.register(Team)
class TeamAdmin(admin.ModelAdmin): class TeamAdmin(admin.ModelAdmin):

7
rog/forms.py Normal file
View File

@ -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ファイル")

View File

@ -489,6 +489,17 @@ class EntrySerializer(serializers.ModelSerializer):
event = data.get('event') event = data.get('event')
category = data.get('category') category = data.get('category')
entry_date = data.get('date') 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 owner = self.context['request'].user
zekken_number = data.get('zekken_number') zekken_number = data.get('zekken_number')
@ -534,6 +545,11 @@ class EntrySerializer(serializers.ModelSerializer):
ret['event'] = NewEvent2Serializer(instance.event).data ret['event'] = NewEvent2Serializer(instance.event).data
ret['category'] = NewCategorySerializer(instance.category).data ret['category'] = NewCategorySerializer(instance.category).data
ret['owner'] = CustomUserSerializer(instance.owner).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 return ret
#if isinstance(ret['date'], datetime): #if isinstance(ret['date'], datetime):

View File

@ -0,0 +1,24 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_urls %}
{% block content %}
<div id="content-main">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="module aligned">
{% for field in form %}
<div class="form-row">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
</fieldset>
<div class="submit-row">
<input type="submit" value="CSVアップロード" class="default" name="_save">
</div>
</form>
</div>
{% endblock %}

View File

@ -9,7 +9,13 @@ from .views import TestActionViewSet
from .views import OwnerEntriesView, OwnerTeamsView, OwnerMembersView from .views import OwnerEntriesView, OwnerTeamsView, OwnerMembersView
from django.urls import path
from . import views
#from .views import NewEvent2AdminView
router = DefaultRouter() router = DefaultRouter()
router.register(r'newevent2', views.NewEvent2ViewSet)
router.register(prefix='location', viewset=LocationViewSet, basename='location') router.register(prefix='location', viewset=LocationViewSet, basename='location')
router.register(prefix='location_line', viewset=Location_lineViewSet, basename="location_line") 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<team_id>\d+)/members', MemberViewSet, basename='team-members') router.register(r'teams/(?P<team_id>\d+)/members', MemberViewSet, basename='team-members')
router.register(r'categories-viewset', NewCategoryViewSet) router.register(r'categories-viewset', NewCategoryViewSet)
router.register(r'newevent2', views.NewEvent2ViewSet)
# Akira 追加 # Akira 追加
# GET /api/members/<member_id>/user/: 特定のメンバーのユーザー情報を取得 # GET /api/members/<member_id>/user/: 特定のメンバーのユーザー情報を取得
@ -96,5 +103,9 @@ urlpatterns += [
path('reset-password/<uidb64>/<token>/', PasswordResetConfirmView.as_view(), name='password_reset_confirm'), path('reset-password/<uidb64>/<token>/', PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
path('users/<int:user_id>/last-goal/', UserLastGoalTimeView.as_view(), name='user-last-goal-time'), path('users/<int:user_id>/last-goal/', UserLastGoalTimeView.as_view(), name='user-last-goal-time'),
path('teams/<int:team_id>/entries/', TeamEntriesView.as_view(), name='team-entries'), path('teams/<int:team_id>/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),
] ]

View File

@ -70,9 +70,127 @@ from django.contrib.gis.geos import Point
from django.contrib.gis.db.models.functions import Distance from django.contrib.gis.db.models.functions import Distance
from django.utils.dateparse import parse_date 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__) 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): class LocationViewSet(viewsets.ModelViewSet):
queryset=Location.objects.all() queryset=Location.objects.all()
@ -1060,12 +1178,12 @@ class RegistrationView(APIView):
class NewEvent2ViewSet(viewsets.ModelViewSet): class NewEvent2ViewSet(viewsets.ModelViewSet):
queryset = NewEvent2.objects.all() queryset = NewEvent2.objects.all()
serializer_class = NewEvent2Serializer serializer_class = NewEvent2Serializer
permission_classes = [IsAuthenticated] permission_classes = [permissions.IsAuthenticated]
class NewEvent2ListView(generics.ListAPIView): class NewEvent2ListView(generics.ListAPIView):
queryset = NewEvent2.objects.all() queryset = NewEvent2.objects.all()
serializer_class = NewEvent2Serializer serializer_class = NewEvent2Serializer
permission_classes = [IsAuthenticated] permission_classes = [permissions.IsAuthenticated]
class NewEventViewSet(viewsets.ModelViewSet): class NewEventViewSet(viewsets.ModelViewSet):
queryset = NewEvent.objects.all() queryset = NewEvent.objects.all()
@ -1370,6 +1488,15 @@ class EntryViewSet(viewsets.ModelViewSet):
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data) serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) 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: try:
self.perform_create(serializer) self.perform_create(serializer)
headers = self.get_success_headers(serializer.data) headers = self.get_success_headers(serializer.data)