mission
This commit is contained in:
parent
0216751834
commit
add9caf0f2
@ -66,7 +66,7 @@ class MangaDetailSerializer(serializers.ModelSerializer):
|
||||
return dict(models.Manga.MANGA_GENRE_CHOICES).get(obj.genre)
|
||||
|
||||
def get_status_display(self, obj):
|
||||
return dict(models.Manga.CONTENT_STATUS_CHOICES).get(obj.status, obj.status)
|
||||
return dict(models.Manga.MANGA_STATUS_CHOICES).get(obj.status, obj.status)
|
||||
|
||||
class MangaChapterSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
@ -52,6 +52,7 @@ INSTALLED_APPS = [
|
||||
'content',
|
||||
'character',
|
||||
'entertainment',
|
||||
'mission',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
||||
@ -31,5 +31,6 @@ urlpatterns = [
|
||||
path('content/', include('content.urls')),
|
||||
path('character/', include('character.urls')),
|
||||
path('entertainment/', include('entertainment.urls')),
|
||||
path('mission/', include('mission.urls')),
|
||||
|
||||
]+ debug_toolbar_urls() + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
0
freekake_api/mission/__init__.py
Normal file
0
freekake_api/mission/__init__.py
Normal file
3
freekake_api/mission/admin.py
Normal file
3
freekake_api/mission/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
freekake_api/mission/apps.py
Normal file
6
freekake_api/mission/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class MissionConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'mission'
|
||||
99
freekake_api/mission/models.py
Normal file
99
freekake_api/mission/models.py
Normal file
@ -0,0 +1,99 @@
|
||||
from django.db import models
|
||||
from django.core.exceptions import ValidationError
|
||||
from django_softdelete.models import SoftDeleteModel
|
||||
from PIL import Image
|
||||
|
||||
def validate_image_size(image):
|
||||
if image.size > 1 * 1024 * 1024:
|
||||
raise ValidationError("File size exceeds 1MB.")
|
||||
|
||||
def validate_image_ext(image):
|
||||
allowed_extensions = ['png', 'jpg', 'jpeg']
|
||||
ext = image.name.split('.')[-1].lower()
|
||||
if ext not in allowed_extensions:
|
||||
raise ValidationError("Invalid file type. Only PNG or JPEG allowed.")
|
||||
|
||||
def validate_image(image):
|
||||
try:
|
||||
img = Image.open(image)
|
||||
img.verify()
|
||||
except Exception:
|
||||
raise ValidationError("Invalid image file.")
|
||||
|
||||
def validate_file_size(image):
|
||||
if image.size > 1 * 1024 * 1024:
|
||||
raise ValidationError("File size exceeds 1MB.")
|
||||
|
||||
class Mission(SoftDeleteModel):
|
||||
|
||||
MISSION_CATEGORY_CHOICES = [
|
||||
('daily','Harian'),
|
||||
('weekly', 'Mingguan'),
|
||||
('monthly', 'Bulanan'),
|
||||
]
|
||||
|
||||
MISSION_TASK_CHOICES = [
|
||||
('scan-qr','Scan QR'),
|
||||
('upload-photo','Upload Foto'),
|
||||
]
|
||||
|
||||
MISSION_STATUS_CHOICES = [
|
||||
('draft', 'Draf'),
|
||||
('published', 'Dipublikasikan'),
|
||||
('archived', 'Diarsipkan'),
|
||||
]
|
||||
|
||||
name = models.CharField(max_length=255, null=False)
|
||||
description = models.CharField(max_length=1000, null=True, blank=True)
|
||||
|
||||
featured_image = models.ImageField(
|
||||
max_length=255,
|
||||
upload_to="uploads/missions/",
|
||||
validators=[validate_image_size, validate_image_ext, validate_image],
|
||||
null=False, blank=False)
|
||||
|
||||
coin = models.IntegerField()
|
||||
point = models.IntegerField()
|
||||
|
||||
category = models.CharField(max_length=50, choices=MISSION_CATEGORY_CHOICES)
|
||||
task = models.CharField(max_length=50, choices=MISSION_TASK_CHOICES)
|
||||
|
||||
date_valid = models.DateField(null=True, blank=True)
|
||||
|
||||
time_from_valid = models.TimeField(null=True, blank=True)
|
||||
time_to_valid = models.TimeField(null=True, blank=True)
|
||||
|
||||
status = models.CharField(max_length=50, choices=MISSION_STATUS_CHOICES, default='draft')
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
posted_at = models.DateTimeField(null=True, blank=True)
|
||||
archived_at = models.DateTimeField(null=True, blank=True)
|
||||
|
||||
class MissionLog(SoftDeleteModel):
|
||||
MISSION_LOG_STATUS_CHOICES = [
|
||||
('visited', 'Baru Dikunjungi'),
|
||||
('completed', 'Sudah Selesai'),
|
||||
('claimed', 'Hadiah Sudah Diambil'),
|
||||
]
|
||||
|
||||
mission = models.ForeignKey(Mission, related_name='logs', on_delete=models.CASCADE)
|
||||
user_id = models.IntegerField()
|
||||
|
||||
status = models.CharField(max_length=50, choices=MISSION_LOG_STATUS_CHOICES)
|
||||
|
||||
coin = models.IntegerField()
|
||||
point = models.IntegerField()
|
||||
|
||||
image_log = models.ImageField(
|
||||
max_length=255,
|
||||
upload_to="uploads/mission_logs/",
|
||||
validators=[validate_image_size, validate_image_ext, validate_image],
|
||||
null=False, blank=False)
|
||||
|
||||
completed_at = models.DateTimeField(null=True, blank=True)
|
||||
claimed_at = models.DateTimeField(null=True, blank=True)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
138
freekake_api/mission/serializers.py
Normal file
138
freekake_api/mission/serializers.py
Normal file
@ -0,0 +1,138 @@
|
||||
from django.forms import ImageField, FileField
|
||||
from rest_framework import serializers
|
||||
from mission import models
|
||||
|
||||
class MissionSerializer(serializers.ModelSerializer):
|
||||
featured_image = ImageField(max_length=255, allow_empty_file=False)
|
||||
|
||||
class Meta:
|
||||
model = models.Mission
|
||||
fields = [
|
||||
'id',
|
||||
'name',
|
||||
'description',
|
||||
'featured_image',
|
||||
'coin',
|
||||
'point',
|
||||
'category',
|
||||
'task',
|
||||
'status',
|
||||
'date_valid',
|
||||
'time_from_valid',
|
||||
'time_to_valid',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
request_method = self.context.get('request').method if self.context.get('request') else None
|
||||
|
||||
if request_method in ['PUT', 'PATCH']:
|
||||
self.fields['featured_image'].required = False
|
||||
|
||||
class MissionDetailSerializer(serializers.ModelSerializer):
|
||||
featured_image = ImageField(max_length=255, allow_empty_file=False)
|
||||
category_display = serializers.SerializerMethodField()
|
||||
task_display = serializers.SerializerMethodField()
|
||||
status_display = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = models.Mission
|
||||
fields = [
|
||||
'id',
|
||||
'name',
|
||||
'description',
|
||||
'featured_image',
|
||||
'coin',
|
||||
'point',
|
||||
'category',
|
||||
'task',
|
||||
'status',
|
||||
'date_valid',
|
||||
'time_from_valid',
|
||||
'time_to_valid',
|
||||
'category_display',
|
||||
'task_display',
|
||||
'status_display',
|
||||
]
|
||||
|
||||
def validate(self, data):
|
||||
category = data.get('category')
|
||||
task = data.get('task')
|
||||
|
||||
allowed_categories = dict(models.Mission.MISSION_CATEGORY_CHOICES).get(category, [])
|
||||
valid_category_keys = [key for key, _ in allowed_categories]
|
||||
|
||||
if category not in valid_category_keys:
|
||||
raise serializers.ValidationError({"category": "Invalid category."})
|
||||
|
||||
allowed_tasks = dict(models.Mission.MISSION_TASK_CHOICES).get(task, [])
|
||||
valid_task_keys = [key for key, _ in allowed_tasks]
|
||||
|
||||
if task not in valid_task_keys:
|
||||
raise serializers.ValidationError({"task": "Invalid task."})
|
||||
|
||||
return data
|
||||
|
||||
def get_category_display(self, obj):
|
||||
return obj.get_category_display()
|
||||
|
||||
def get_task_display(self, obj):
|
||||
return obj.get_task_display()
|
||||
|
||||
def get_status_display(self, obj):
|
||||
return obj.get_status_display()
|
||||
|
||||
class MissionLogSerializer(serializers.ModelSerializer):
|
||||
image_log = ImageField(max_length=255, allow_empty_file=False)
|
||||
|
||||
class Meta:
|
||||
model = models.MissionLog
|
||||
fields = [
|
||||
'mission',
|
||||
'user_id',
|
||||
'status',
|
||||
'coin',
|
||||
'point',
|
||||
'image_log',
|
||||
'completed_at',
|
||||
'claimed_at',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
request_method = self.context.get('request').method if self.context.get('request') else None
|
||||
|
||||
if request_method in ['PUT', 'PATCH']:
|
||||
self.fields['image_log'].required = False
|
||||
|
||||
class MissionLogDetailSerializer(serializers.ModelSerializer):
|
||||
image_log = ImageField(max_length=255, allow_empty_file=False)
|
||||
status_display = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = models.MissionLog
|
||||
fields = [
|
||||
'id',
|
||||
'mission',
|
||||
'user_id',
|
||||
'status',
|
||||
'coin',
|
||||
'point',
|
||||
'image_log',
|
||||
'completed_at',
|
||||
'claimed_at',
|
||||
'status_display',
|
||||
]
|
||||
|
||||
def validate(self, data):
|
||||
status = data.get('status')
|
||||
|
||||
allowed_statuses = dict(models.MissionLog.MISSION_LOG_STATUS_CHOICES).keys()
|
||||
|
||||
if status not in allowed_statuses:
|
||||
raise serializers.ValidationError({"status": "Invalid status."})
|
||||
|
||||
return data
|
||||
|
||||
def get_status_display(self, obj):
|
||||
return obj.get_status_display()
|
||||
3
freekake_api/mission/tests.py
Normal file
3
freekake_api/mission/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
14
freekake_api/mission/urls.py
Normal file
14
freekake_api/mission/urls.py
Normal file
@ -0,0 +1,14 @@
|
||||
from django.urls import path
|
||||
from mission import views
|
||||
|
||||
urlpatterns = [
|
||||
path('missions/', views.MissionList.as_view()),
|
||||
path('missions/<int:pk>/', views.MissionDetail.as_view()),
|
||||
|
||||
path('categories/', views.MissionCategoryChoices.as_view()),
|
||||
path('tasks/', views.MissionTaskChoices.as_view()),
|
||||
path('statuses/', views.MissionStatusChoices.as_view()),
|
||||
|
||||
path('mission-logs/', views.MissionLogList.as_view()),
|
||||
path('mission-logs/<int:pk>/', views.MissionLogDetail.as_view()),
|
||||
]
|
||||
82
freekake_api/mission/views.py
Normal file
82
freekake_api/mission/views.py
Normal file
@ -0,0 +1,82 @@
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import generics, filters, views
|
||||
from django_filters.rest_framework import DjangoFilterBackend, FilterSet, CharFilter, ChoiceFilter
|
||||
|
||||
from mission import models, serializers
|
||||
|
||||
class MissionList(generics.ListCreateAPIView):
|
||||
queryset = models.Mission.objects.all()
|
||||
serializer_class = serializers.MissionSerializer
|
||||
filter_backends = [filters.SearchFilter, filters.OrderingFilter, DjangoFilterBackend]
|
||||
search_fields = ['name']
|
||||
filterset_fields = ['category', 'task', 'status']
|
||||
ordering_fields = '__all__'
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.request.method in ['GET']:
|
||||
return serializers.MissionDetailSerializer
|
||||
return serializers.MissionSerializer
|
||||
|
||||
class MissionDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
queryset = models.Mission.objects.all()
|
||||
serializer_class = serializers.MissionDetailSerializer
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.request.method in ['GET']:
|
||||
return serializers.MissionDetailSerializer
|
||||
return serializers.MissionSerializer
|
||||
|
||||
class MissionCategoryChoices(views.APIView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
return Response({
|
||||
"count": len(models.Mission.MISSION_CATEGORY_CHOICES),
|
||||
"results": [
|
||||
{"value": choice[0], "label": choice[1]}
|
||||
for choice in models.Mission.MISSION_CATEGORY_CHOICES
|
||||
]
|
||||
})
|
||||
|
||||
class MissionTaskChoices(views.APIView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
return Response({
|
||||
"count": len(models.Mission.MISSION_TASK_CHOICES),
|
||||
"results": [
|
||||
{"value": choice[0], "label": choice[1]}
|
||||
for choice in models.Mission.MISSION_TASK_CHOICES
|
||||
]
|
||||
})
|
||||
|
||||
class MissionStatusChoices(views.APIView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
return Response({
|
||||
"count": len(models.Mission.MISSION_STATUS_CHOICES),
|
||||
"results": [
|
||||
{"value": choice[0], "label": choice[1]}
|
||||
for choice in models.Mission.MISSION_STATUS_CHOICES
|
||||
]
|
||||
})
|
||||
|
||||
class MissionLogList(generics.ListCreateAPIView):
|
||||
queryset = models.MissionLog.objects.all()
|
||||
serializer_class = serializers.MissionLogSerializer
|
||||
filter_backends = [filters.SearchFilter, filters.OrderingFilter, DjangoFilterBackend]
|
||||
search_fields = ['user_id']
|
||||
filterset_fields = ['mission', 'user_id', 'status']
|
||||
ordering_fields = '__all__'
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.request.method in ['GET']:
|
||||
return serializers.MissionLogDetailSerializer
|
||||
return serializers.MissionLogSerializer
|
||||
|
||||
class MissionLogDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
queryset = models.MissionLog.objects.all()
|
||||
serializer_class = serializers.MissionLogSerializer
|
||||
|
||||
def get_serializer_class(self):
|
||||
serializer_class = self.serializer_class
|
||||
|
||||
if self.request.method in ['GET']:
|
||||
return serializers.MissionLogDetailSerializer
|
||||
|
||||
return serializer_class
|
||||
Loading…
Reference in New Issue
Block a user