pre release 20240903
This commit is contained in:
@ -252,3 +252,5 @@ PASSWORD_HASHERS = [
|
|||||||
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
|
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
BLACKLISTED_IPS = ['44.230.58.114'] # ブロックしたい IP アドレスをここにリストとして追加
|
||||||
|
|
||||||
|
|||||||
43
rog/admin.py
43
rog/admin.py
@ -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
7
rog/forms.py
Normal 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ファイル")
|
||||||
|
|
||||||
@ -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):
|
||||||
|
|||||||
24
rog/templates/admin/csv_upload.html
Normal file
24
rog/templates/admin/csv_upload.html
Normal 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 %}
|
||||||
11
rog/urls.py
11
rog/urls.py
@ -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),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|||||||
131
rog/views.py
131
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.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)
|
||||||
|
|||||||
Reference in New Issue
Block a user