added full event logic, fixed clan creation, minor changes

This commit is contained in:
Alexander Karpov 2022-10-09 00:42:30 +03:00
parent 8beee17e31
commit 12f81dbf3f
12 changed files with 218 additions and 53 deletions

View File

@ -1,18 +1,26 @@
from django.urls import path, include from django.urls import path, include
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
from events.api.views import ListCreateEventApi, RetireUpdateDeleteEventApi from events.api.views import (
from marketplace.api.views import ListCreateProductApi, RetireUpdateDestroyProductApi ListCreateEventApi,
RetrieveUpdateDeleteEventApi,
ListPlannedEvents,
RetrieveSubmitDeleteEventAttendance,
ListAttendedWorkersApi,
SubmitWorkerAttendedEvent,
)
from marketplace.api.views import ListCreateProductApi, RetrieveUpdateDestroyProductApi
from users.api.views import ( from users.api.views import (
ListCreateUserApi, ListCreateUserApi,
RetireUpdateDeleteUserApi, RetrieveUpdateDeleteUserApi,
ListCreateDepartmentApi, ListCreateDepartmentApi,
RetireUpdateDeleteDepartmentApi, RetrieveUpdateDeleteDepartmentApi,
ListCreateStreamApi, ListCreateStreamApi,
RetireUpdateDeleteStreamApi, RetrieveUpdateDeleteStreamApi,
ListCreateCommandApi, ListCreateCommandApi,
RetireUpdateDeleteCommandApi, RetrieveUpdateDeleteCommandApi,
CreateSeasonApi, CreateSeasonApi,
ListClansApiView,
) )
urlpatterns = [ urlpatterns = [
@ -32,9 +40,29 @@ urlpatterns = [
path("", ListCreateEventApi.as_view(), name="list_create_event"), path("", ListCreateEventApi.as_view(), name="list_create_event"),
path( path(
"<str:slug>", "<str:slug>",
RetireUpdateDeleteEventApi.as_view(), RetrieveUpdateDeleteEventApi.as_view(),
name="get_update_delete_event", name="get_update_delete_event",
), ),
path(
"attendance/",
ListPlannedEvents.as_view(),
name="list_event_attendance",
),
path(
"attendance/<str:slug>/list/",
ListAttendedWorkersApi.as_view(),
name="list_event_attendance",
),
path(
"attendance/<str:slug>/submit/",
SubmitWorkerAttendedEvent.as_view(),
name="submit_event_attendance",
),
path(
"attendance/<str:slug>",
RetrieveSubmitDeleteEventAttendance.as_view(),
name="get_submit_delete_event_attendance",
),
] ]
), ),
), ),
@ -49,7 +77,7 @@ urlpatterns = [
), ),
path( path(
"product/<str:slug>", "product/<str:slug>",
RetireUpdateDestroyProductApi.as_view(), RetrieveUpdateDestroyProductApi.as_view(),
name="get_update_destroy_product", name="get_update_destroy_product",
), ),
] ]
@ -62,7 +90,7 @@ urlpatterns = [
path("", ListCreateUserApi.as_view(), name="list_create_user"), path("", ListCreateUserApi.as_view(), name="list_create_user"),
path( path(
"<str:username>", "<str:username>",
RetireUpdateDeleteUserApi.as_view(), RetrieveUpdateDeleteUserApi.as_view(),
name="get_update_delete_user", name="get_update_delete_user",
), ),
path( path(
@ -77,7 +105,7 @@ urlpatterns = [
), ),
path( path(
"department/<int:pk>", "department/<int:pk>",
RetireUpdateDeleteDepartmentApi.as_view(), RetrieveUpdateDeleteDepartmentApi.as_view(),
name="get_update_delete_department", name="get_update_delete_department",
), ),
path( path(
@ -87,7 +115,7 @@ urlpatterns = [
), ),
path( path(
"stream/<int:pk>", "stream/<int:pk>",
RetireUpdateDeleteStreamApi.as_view(), RetrieveUpdateDeleteStreamApi.as_view(),
name="get_update_delete_stream", name="get_update_delete_stream",
), ),
path( path(
@ -97,7 +125,7 @@ urlpatterns = [
), ),
path( path(
"command/<int:pk>", "command/<int:pk>",
RetireUpdateDeleteCommandApi.as_view(), RetrieveUpdateDeleteCommandApi.as_view(),
name="get_update_delete_command", name="get_update_delete_command",
), ),
] ]
@ -105,6 +133,11 @@ urlpatterns = [
), ),
path( path(
"season/", "season/",
include([path("", CreateSeasonApi.as_view(), name="create new season")]), include(
[
path("", CreateSeasonApi.as_view(), name="create new season"),
path("clans/", ListClansApiView.as_view()),
]
),
), ),
] ]

View File

@ -62,7 +62,7 @@ DJANGO_APPS = [
] ]
THIRD_PARTY_APPS = ["rest_framework", "corsheaders", "django_celery_beat", "drf_yasg"] THIRD_PARTY_APPS = ["rest_framework", "corsheaders", "django_celery_beat", "drf_yasg"]
LOCAL_APPS = ["users", "marketplace", "events"] LOCAL_APPS = ["users", "marketplace", "events", "blockchain"]
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps # https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS

View File

@ -1,7 +1,9 @@
from rest_framework import serializers from rest_framework import serializers
from rest_framework.generics import get_object_or_404
from events.models import Event from events.models import Event, EventAttendance
from users.api.serializers import UserSerializer from users.api.serializers import UserSerializer
from users.models import User
class EventSerializer(serializers.ModelSerializer): class EventSerializer(serializers.ModelSerializer):
@ -30,3 +32,45 @@ class EventSerializer(serializers.ModelSerializer):
return Event.objects.create( return Event.objects.create(
**validated_data, creator=self.context["request"].user **validated_data, creator=self.context["request"].user
) )
class EventAttendanceSerializer(serializers.ModelSerializer):
class Meta:
model = EventAttendance
fields = ["id", "event_slug", "worker_username", "attended"]
extra_kwargs = {
"id": {"read_only": True},
"event_slug": {"read_only": True},
"worker_username": {"read_only": True},
"attended": {"read_only": True},
}
def create(self, validated_data):
return EventAttendance.objects.get_or_create(
worker=self.context["request"].user,
event=get_object_or_404(
Event, slug=self.context["request"].parser_context["kwargs"]["slug"]
),
)[0]
class SubmitUserAttendedSerializer(serializers.Serializer):
username = serializers.CharField(max_length=200)
def create(self, validated_data):
event = get_object_or_404(
Event, slug=self.context["request"].parser_context["kwargs"]["slug"]
)
ea = EventAttendance.objects.get_or_create(
event=event,
worker=get_object_or_404(User, username=validated_data["username"]),
)[0]
if not ea.attended:
ea.attended = True
ea.save()
ea.event.attended += 1
ea.event.save()
return ea
def update(self, instance, validated_data):
pass

View File

@ -2,13 +2,19 @@ from datetime import datetime
import pytz import pytz
from rest_framework import generics from rest_framework import generics
from rest_framework.exceptions import NotFound
from rest_framework.generics import get_object_or_404 from rest_framework.generics import get_object_or_404
from rest_framework.parsers import FormParser, MultiPartParser from rest_framework.parsers import FormParser, MultiPartParser
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from common.permissions import IsManager from common.permissions import IsManager, IsWorker
from events.api.serializers import EventSerializer from events.api.serializers import (
from events.models import Event EventSerializer,
EventAttendanceSerializer,
SubmitUserAttendedSerializer,
)
from events.models import Event, EventAttendance
class ListCreateEventApi(generics.ListCreateAPIView): class ListCreateEventApi(generics.ListCreateAPIView):
@ -20,7 +26,7 @@ class ListCreateEventApi(generics.ListCreateAPIView):
) )
class RetireUpdateDeleteEventApi(generics.RetrieveUpdateDestroyAPIView): class RetrieveUpdateDeleteEventApi(generics.RetrieveUpdateDestroyAPIView):
def get_object(self): def get_object(self):
event = get_object_or_404( event = get_object_or_404(
Event, Event,
@ -32,3 +38,51 @@ class RetireUpdateDeleteEventApi(generics.RetrieveUpdateDestroyAPIView):
parser_classes = [FormParser, MultiPartParser] parser_classes = [FormParser, MultiPartParser]
permission_classes = [IsAuthenticated, IsManager] permission_classes = [IsAuthenticated, IsManager]
queryset = Event.objects.all() queryset = Event.objects.all()
class RetrieveSubmitDeleteEventAttendance(
generics.CreateAPIView, generics.RetrieveDestroyAPIView
):
"""Gets/Submits/Deletes that user is planing to go on event. Only works for worker"""
def get_object(self):
event = get_object_or_404(
Event,
slug=self.request.parser_context["kwargs"]["slug"],
)
if EventAttendance.objects.filter(
event=event, worker=self.request.user
).exists():
return EventAttendance.objects.get(event=event, worker=self.request.user)
raise NotFound
serializer_class = EventAttendanceSerializer
permission_classes = [IsAuthenticated, IsWorker]
class ListPlannedEvents(generics.ListAPIView):
"""Lists events that worker is planning to attend. Only works for worker"""
def get_queryset(self):
return self.request.user.events.filter(
attended=False,
event__starts__gte=datetime.now(pytz.timezone("Europe/Moscow")),
)
serializer_class = EventAttendanceSerializer
permission_classes = [IsAuthenticated, IsWorker]
class ListAttendedWorkersApi(generics.ListAPIView):
def get_queryset(self):
return EventAttendance.objects.filter(
event__slug=self.request.parser_context["kwargs"]["slug"]
)
serializer_class = EventAttendanceSerializer
permission_classes = [IsAuthenticated, IsManager]
class SubmitWorkerAttendedEvent(generics.CreateAPIView):
serializer_class = SubmitUserAttendedSerializer
permission_classes = [IsAuthenticated, IsManager]

View File

@ -32,5 +32,20 @@ class EventAttendance(models.Model):
token = models.CharField(blank=False, unique=True, max_length=128) token = models.CharField(blank=False, unique=True, max_length=128)
attended = models.BooleanField(default=False) attended = models.BooleanField(default=False)
@property
def event_slug(self):
return self.event.slug
@property
def worker_username(self):
return self.worker.username
def username(self):
return self.worker_username
def __str__(self): def __str__(self):
return f"{self.worker.name} attendance on {self.event.name}" return f"{self.worker.name} attendance on {self.event.name}"
class Meta:
ordering = ["event__starts"]
unique_together = ["event", "worker"]

View File

@ -7,15 +7,16 @@ from utils.generators import generate_charset
from .models import EventAttendance, Event from .models import EventAttendance, Event
@receiver(pre_save, sender=EventAttendance) @receiver(post_save, sender=EventAttendance)
def create_attendance(sender, instance, **kwargs): def create_attendance(sender, instance, created, **kwargs):
token = generate_charset(25) if created:
while EventAttendance.objects.filter(token=token).exists():
token = generate_charset(25) token = generate_charset(25)
instance.token = token while EventAttendance.objects.filter(token=token).exists():
token = generate_charset(25)
instance.token = token
instance.event.planning += 1 instance.event.planning += 1
instance.event.save(update_fileds=["planning"]) instance.event.save(update_fields=["planning", "token"])
@receiver(post_save, sender=Event) @receiver(post_save, sender=Event)
@ -38,4 +39,4 @@ def process_event(sender, instance, created, **kwargs):
@receiver(post_delete, sender=EventAttendance) @receiver(post_delete, sender=EventAttendance)
def delete_attendance(sender, instance, **kwargs): def delete_attendance(sender, instance, **kwargs):
instance.event.planning -= 1 instance.event.planning -= 1
instance.event.save(update_fileds=["planning"]) instance.event.save(update_fields=["planning"])

View File

@ -15,7 +15,7 @@ class ListCreateProductApi(generics.ListCreateAPIView):
queryset = Product.objects.all() queryset = Product.objects.all()
class RetireUpdateDestroyProductApi(generics.RetrieveUpdateDestroyAPIView): class RetrieveUpdateDestroyProductApi(generics.RetrieveUpdateDestroyAPIView):
serializer_class = ProductSerializer serializer_class = ProductSerializer
parser_classes = [FormParser, MultiPartParser] parser_classes = [FormParser, MultiPartParser]
permission_classes = [IsAuthenticated, IsManager] permission_classes = [IsAuthenticated, IsManager]

View File

@ -1,6 +1,6 @@
from rest_framework import serializers from rest_framework import serializers
from ..services import create_season from ..services import create_season
from users.models import User from users.models import User, Clan
from users.models import User, Department, Stream, Command from users.models import User, Department, Stream, Command
@ -18,10 +18,12 @@ class UserSerializer(serializers.ModelSerializer):
"wallet_public_key", "wallet_public_key",
"command", "command",
"department", "department",
"clan_name",
] ]
extra_kwargs = { extra_kwargs = {
"password": {"write_only": True}, "password": {"write_only": True},
"wallet_public_key": {"read_only": True}, "wallet_public_key": {"read_only": True},
"clan_name": {"read_only": True},
"department": {"read_only": True}, "department": {"read_only": True},
} }
@ -34,9 +36,10 @@ class UserSerializer(serializers.ModelSerializer):
class CreateSeasonSerializer(serializers.Serializer): class CreateSeasonSerializer(serializers.Serializer):
created = serializers.BooleanField(read_only=True) created = serializers.BooleanField(read_only=True)
def create(self, *args, **kwargs): def create(self, *args, **kwargs):
create_season() create_season()
return {'created': True} return {"created": True}
class CommandSerializer(serializers.ModelSerializer): class CommandSerializer(serializers.ModelSerializer):
@ -75,3 +78,11 @@ class DepartmentSerializer(serializers.ModelSerializer):
"id": {"read_only": True}, "id": {"read_only": True},
"streams": {"read_only": True}, "streams": {"read_only": True},
} }
class ClanSerializer(serializers.ModelSerializer):
users = UserSerializer(many=True)
class Meta:
model = Clan
fields = ["name", "users"]

View File

@ -3,14 +3,15 @@ from rest_framework.generics import get_object_or_404
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from common.permissions import IsAdmin from common.permissions import IsAdmin
from users.api.serializers import CreateSeasonSerializer
from users.api.serializers import ( from users.api.serializers import (
UserSerializer, UserSerializer,
DepartmentSerializer, DepartmentSerializer,
StreamSerializer, StreamSerializer,
CommandSerializer, CommandSerializer,
CreateSeasonSerializer,
ClanSerializer,
) )
from users.models import User, Department, Stream, Command from users.models import User, Department, Stream, Command, Clan
class ListCreateUserApi(generics.ListCreateAPIView): class ListCreateUserApi(generics.ListCreateAPIView):
@ -24,7 +25,7 @@ class CreateSeasonApi(generics.CreateAPIView):
# permission_classes = [IsAuthenticated, IsAdmin] # permission_classes = [IsAuthenticated, IsAdmin]
class RetireUpdateDeleteUserApi(generics.RetrieveUpdateDestroyAPIView): class RetrieveUpdateDeleteUserApi(generics.RetrieveUpdateDestroyAPIView):
def get_object(self): def get_object(self):
user = get_object_or_404( user = get_object_or_404(
User, User,
@ -43,7 +44,7 @@ class ListCreateDepartmentApi(generics.ListCreateAPIView):
queryset = Department.objects.all() queryset = Department.objects.all()
class RetireUpdateDeleteDepartmentApi(generics.RetrieveUpdateDestroyAPIView): class RetrieveUpdateDeleteDepartmentApi(generics.RetrieveUpdateDestroyAPIView):
lookup_field = "pk" lookup_field = "pk"
serializer_class = DepartmentSerializer serializer_class = DepartmentSerializer
permission_classes = [IsAuthenticated, IsAdmin] permission_classes = [IsAuthenticated, IsAdmin]
@ -55,7 +56,7 @@ class ListCreateStreamApi(ListCreateDepartmentApi):
queryset = Stream.objects.all() queryset = Stream.objects.all()
class RetireUpdateDeleteStreamApi(RetireUpdateDeleteDepartmentApi): class RetrieveUpdateDeleteStreamApi(RetrieveUpdateDeleteDepartmentApi):
serializer_class = StreamSerializer serializer_class = StreamSerializer
queryset = Stream.objects.all() queryset = Stream.objects.all()
@ -65,6 +66,12 @@ class ListCreateCommandApi(ListCreateDepartmentApi):
queryset = Command.objects.all() queryset = Command.objects.all()
class RetireUpdateDeleteCommandApi(RetireUpdateDeleteDepartmentApi): class RetrieveUpdateDeleteCommandApi(RetrieveUpdateDeleteDepartmentApi):
serializer_class = CommandSerializer serializer_class = CommandSerializer
queryset = Command.objects.all() queryset = Command.objects.all()
class ListClansApiView(generics.ListAPIView):
serializer_class = ClanSerializer
queryset = Clan.objects.all()
permission_classes = [IsAuthenticated, IsAdmin]

View File

@ -22,7 +22,7 @@ class User(AbstractUser):
type = models.CharField( type = models.CharField(
max_length=6, choices=WorkerType.choices, default=WorkerType.WORKER max_length=6, choices=WorkerType.choices, default=WorkerType.WORKER
) )
clan = models.ForeignKey("users.Clan", on_delete=models.SET_NULL, null=True) clan = models.ForeignKey("users.Clan", related_name="users", on_delete=models.SET_NULL, null=True)
command = models.ForeignKey( command = models.ForeignKey(
"users.Command", related_name="workers", on_delete=models.CASCADE "users.Command", related_name="workers", on_delete=models.CASCADE
) )
@ -47,6 +47,10 @@ class User(AbstractUser):
def department(self): def department(self):
return self.command.stream.department.name return self.command.stream.department.name
@property
def clan_name(self):
return self.clan.name
class Meta: class Meta:
ordering = ["-id"] ordering = ["-id"]

View File

@ -12,10 +12,10 @@ def end_season():
mx_value = -1 mx_value = -1
mx_clan = None mx_clan = None
for clan in Clan.objects.all(): for clan in Clan.objects.all():
if sum(map(lambda user: user.respect, clan.user_set.all())) > mx_value: if sum(map(lambda user: user.respect, clan.users.all())) > mx_value:
mx_value = sum(map(lambda user: user.respect, clan.user_set.all())) mx_value = sum(map(lambda user: user.respect, clan.users.all()))
mx_clan = clan mx_clan = clan
for user in mx_clan.user_set.all(): for user in mx_clan.users.all():
transfer_rubbles( transfer_rubbles(
settings.MAIN_WALLET, settings.MAIN_WALLET,
user.wallet_public_key, user.wallet_public_key,
@ -25,7 +25,7 @@ def end_season():
def create_chat(clan: Clan): def create_chat(clan: Clan):
user_list = list(map(lambda user: user.telegram, clan.user_set.all())) user_list = list(map(lambda user: user.telegram, clan.users.all()))
if len(user_list): if len(user_list):
r.post( r.post(
f"{settings.TELEGRAM_API}/create-chat", f"{settings.TELEGRAM_API}/create-chat",
@ -38,15 +38,13 @@ def create_season():
if len(Clan.objects.all()): if len(Clan.objects.all()):
end_season() end_season()
users = list(User.objects.all()) users = list(User.objects.filter(type=User.WorkerType.WORKER))
shuffle(users) shuffle(users)
clan = None clan = None
for index, user in enumerate(users): for index, user in enumerate(users):
if (index % 10 == 0) or (index == len(users) - 1): if index % 5 == 0:
if clan is not None:
create_chat(clan)
clan = Clan.objects.create() clan = Clan.objects.create()
user.clan = clan user.clan = clan
user.save() user.save()
if len(users) % 10 != 0: for clan in Clan.objects.all():
create_chat(clan) create_chat(clan)

View File

@ -5,15 +5,13 @@ from users.models import User
from utils.blockchain import create_wallet from utils.blockchain import create_wallet
@receiver(pre_save, sender=User)
def create_user(sender, instance, **kwargs):
wallet = create_wallet()
instance.wallet_public_key = wallet.publicKey
instance.wallet_private_key = wallet.privateKey
@receiver(post_save, sender=User) @receiver(post_save, sender=User)
def process_user(sender, instance, created, **kwargs): def process_user(sender, instance, created, **kwargs):
if created: if created:
instance.set_password(instance.password) instance.set_password(instance.password)
wallet = create_wallet()
instance.wallet_public_key = wallet.publicKey
instance.wallet_private_key = wallet.privateKey
instance.save() instance.save()