diff --git a/freekake_api/character/serializers.py b/freekake_api/character/serializers.py index 84a5697..db2d64e 100644 --- a/freekake_api/character/serializers.py +++ b/freekake_api/character/serializers.py @@ -27,6 +27,15 @@ class CharacterSkinSerializer(serializers.ModelSerializer): model = models.CharacterSkin fields = ['id', 'name', 'description', 'featured_image', 'featured_icon', 'character'] + 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']: + # Jadikan optional di update + self.fields['featured_image'].required = False + self.fields['featured_icon'].required = False + class CharacterSkinDetailSerializer(serializers.ModelSerializer): character = CharacterSerializer(read_only=True) featured_image = ImageField(max_length=255, allow_empty_file=False) diff --git a/freekake_api/content/models.py b/freekake_api/content/models.py index 4fc3952..96613b3 100644 --- a/freekake_api/content/models.py +++ b/freekake_api/content/models.py @@ -21,8 +21,8 @@ def validate_image(image): raise ValidationError("Invalid image file.") def validate_file_size(image): - if image.size > 10 * 1024 * 1024: - raise ValidationError("File size exceeds 10MB.") + if image.size > 1 * 1024 * 1024: + raise ValidationError("File size exceeds 1MB.") class Content(SoftDeleteModel): @@ -52,7 +52,7 @@ class Content(SoftDeleteModel): ('olahraga', 'Olahraga'), ('waktu-tidur', 'Waktu Tidur'), ('kesehatan-mental', 'Kesehatan Mental'), - ('pendidikan-seksua', 'Pendidikan Seksual'), + ('pendidikan-seksual', 'Pendidikan Seksual'), ('rokok-alkohol-narkoba', 'Bahaya Merokok dan Alkohol serta Pencegahan Narkoba'), ('sistem-imun', 'Sistem Imun') ], @@ -69,7 +69,7 @@ class Content(SoftDeleteModel): ], 'keselamatan': [ ('lalu-lintas', 'Keselamatan Berlalu Lintas'), - ('pertolongan-pertama', 'Pertolongan Petrtama'), + ('pertolongan-pertama', 'Pertolongan Pertama'), ('bencana', 'Kesiapsiagaan Bencana') ] } @@ -87,10 +87,22 @@ class Content(SoftDeleteModel): ('personality-quiz', 'Permainan Tebak Sifat'), ('game-map', 'Peta Permainan'), ('pilihan-ganda', 'Pilihan Ganda'), + ('augmented-reality', 'Augmented Reality'), + ] + + CONTENT_TYPE_CHOICES = [ + ('content','Konten'), + ('advertorial', 'Advertorial'), + ] + + CONTENT_STATUS_CHOICES = [ + ('draft', 'Draf'), + ('published', 'Dipublikasikan'), + ('archived', 'Diarsipkan'), ] title = models.CharField(max_length=255, null=False) - slug = models.SlugField(max_length=255) + slug = models.SlugField(max_length=255, unique=True) featured_image = models.ImageField( max_length=255, @@ -98,24 +110,30 @@ class Content(SoftDeleteModel): validators=[validate_image_size, validate_image_ext, validate_image], null=False, blank=False) - theme = models.CharField(max_length=25, choices=CONTENT_THEME_CHOICES) - topic = models.CharField(max_length=25) - format = models.CharField(max_length=25, choices=CONTENT_FORMAT_CHOICES) + theme = models.CharField(max_length=50, choices=CONTENT_THEME_CHOICES) + topic = models.CharField(max_length=50) + format = models.CharField(max_length=50, choices=CONTENT_FORMAT_CHOICES) description = models.CharField(max_length=255, null=True, blank=True) content = models.TextField(null=True) data = models.JSONField(null=True) - grades = models.JSONField(null=True) + grades = models.JSONField(null=True, blank=True) point = models.IntegerField() coin = models.IntegerField() color = models.CharField(max_length=7, null=True, blank=True) + type = models.CharField(max_length=50, choices=CONTENT_TYPE_CHOICES, default='content') + status = models.CharField(max_length=50, choices=CONTENT_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) + def __str__(self): return self.title @@ -124,7 +142,7 @@ class Media(SoftDeleteModel): name = models.CharField(max_length=255, null=False) media = models.FileField( max_length=255, - upload_to="uploads/medias/", + upload_to="uploads/media/", validators=[validate_file_size], null=False, blank=False) diff --git a/freekake_api/content/serializers.py b/freekake_api/content/serializers.py index 124d9ee..4fadf4d 100644 --- a/freekake_api/content/serializers.py +++ b/freekake_api/content/serializers.py @@ -8,14 +8,69 @@ class ContentSerializer(serializers.ModelSerializer): class Meta: model = models.Content - fields = ['id', 'title', 'slug', 'featured_image', 'theme', 'topic', 'format', 'description','content', 'point', 'coin', 'data', 'grades', 'color'] + fields = [ + 'id', + 'title', + 'slug', + 'featured_image', + 'theme', + 'topic', + 'format', + 'description', + 'content', + 'point', + 'coin', + 'data', + 'grades', + 'color', + 'type', + 'status', + 'posted_at', + 'archived_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['featured_image'].required = False class ContentDetailSerializer(serializers.ModelSerializer): featured_image = ImageField(max_length=255, allow_empty_file=False) + theme_display = serializers.SerializerMethodField() + topic_display = serializers.SerializerMethodField() + format_display = serializers.SerializerMethodField() + type_display = serializers.SerializerMethodField() + status_display = serializers.SerializerMethodField() class Meta: model = models.Content - fields = ['id', 'title', 'slug', 'featured_image', 'theme', 'topic', 'format', 'description','content', 'point', 'coin', 'data', 'grades', 'color'] + fields = [ + 'id', + 'title', + 'slug', + 'featured_image', + 'theme', + 'topic', + 'format', + 'description', + 'content', + 'point', + 'coin', + 'data', + 'grades', + 'color', + 'type', + 'status', + 'posted_at', + 'archived_at', + 'theme_display', + 'topic_display', + 'format_display', + 'type_display', + 'status_display', + ] def validate(self, data): theme = data.get('theme') @@ -29,6 +84,25 @@ class ContentDetailSerializer(serializers.ModelSerializer): return data + def get_theme_display(self, obj): + return dict(models.Content.CONTENT_THEME_CHOICES).get(obj.theme) + + def get_topic_display(self, obj): + # Ambil label topic dari dict berdasarkan theme + topic_choices = dict(models.Content.CONTENT_TOPIC_CHOICES).get(obj.theme, []) + topic_dict = dict(topic_choices) + return topic_dict.get(obj.topic) + + def get_format_display(self, obj): + return dict(models.Content.CONTENT_FORMAT_CHOICES).get(obj.format, obj.format) + + def get_type_display(self, obj): + return dict(models.Content.CONTENT_TYPE_CHOICES).get(obj.type, obj.type) + + def get_status_display(self, obj): + return dict(models.Content.CONTENT_STATUS_CHOICES).get(obj.status, obj.status) + + class ContentMediaSerializer(serializers.ModelSerializer): media = FileField(max_length=255, allow_empty_file=False) diff --git a/freekake_api/content/urls.py b/freekake_api/content/urls.py index 1e8e53b..c2c175a 100644 --- a/freekake_api/content/urls.py +++ b/freekake_api/content/urls.py @@ -8,6 +8,8 @@ urlpatterns = [ path('themes/', views.ContentThemeChoices.as_view()), path('topics//', views.ContentTopicChoices.as_view()), path('formats/', views.ContentFormatChoices.as_view()), + path('types/', views.ContentTypeChoices.as_view()), + path('statuses/', views.ContentStatusChoices.as_view()), path('media/', views.ContentMediaList.as_view()), path('media//', views.ContentMediaDetail.as_view()), diff --git a/freekake_api/content/views.py b/freekake_api/content/views.py index 792422f..eb65f87 100644 --- a/freekake_api/content/views.py +++ b/freekake_api/content/views.py @@ -95,4 +95,24 @@ class ContentMediaDetail(generics.RetrieveUpdateDestroyAPIView): serializer_class = serializers.ContentMediaSerializer filter_backends = [filters.SearchFilter, filters.OrderingFilter] search_fields = ['name'] - ordering_fields = '__all__' \ No newline at end of file + ordering_fields = '__all__' + +class ContentTypeChoices(views.APIView): + def get(self, request, *args, **kwargs): + return Response({ + "count": len(models.Content.CONTENT_TYPE_CHOICES), + "results": [ + {"value": choice[0], "label": choice[1]} + for choice in models.Content.CONTENT_TYPE_CHOICES + ] + }) + +class ContentStatusChoices(views.APIView): + def get(self, request, *args, **kwargs): + return Response({ + "count": len(models.Content.CONTENT_STATUS_CHOICES), + "results": [ + {"value": choice[0], "label": choice[1]} + for choice in models.Content.CONTENT_STATUS_CHOICES + ] + }) \ No newline at end of file