diff --git a/zubhub_backend/zubhub/projects/serializers.py b/zubhub_backend/zubhub/projects/serializers.py index 61d9b1a1..4bc3aa81 100644 --- a/zubhub_backend/zubhub/projects/serializers.py +++ b/zubhub_backend/zubhub/projects/serializers.py @@ -1,20 +1,25 @@ import re -from wsgiref.validate import validator -from django.utils.translation import ugettext_lazy as _ -from rest_framework import serializers -from django.contrib.auth import get_user_model +from math import ceil + +from activities.models import Activity +from creators.serializers import CreatorGroupSerializer, CreatorMinimalSerializer from django.conf import settings +from django.contrib.auth import get_user_model from django.db import transaction -from creators.serializers import CreatorMinimalSerializer, CreatorGroupSerializer -from .models import Project, Comment, Image, StaffPick, Category, Tag, PublishingRule +from django.utils.translation import ugettext_lazy as _ from projects.tasks import filter_spam_task +from rest_framework import serializers + +from .models import Category, Comment, Image, Project, PublishingRule, StaffPick, Tag from .pagination import ProjectNumberPagination -from .utils import update_images, update_tags, parse_comment_trees, get_published_projects_for_user -from .tasks import update_video_url_if_transform_ready, delete_file_task -from math import ceil -from activities.models import Activity -from django.core.exceptions import ValidationError -from django.db.models import Count +from .tasks import delete_file_task, update_video_url_if_transform_ready +from .utils import ( + get_published_projects_for_user, + parse_comment_trees, + update_images, + update_tags, +) + Creator = get_user_model() @@ -23,15 +28,12 @@ class PublishingRuleSerializer(serializers.ModelSerializer): class Meta: model = PublishingRule - fields = [ - "type", - "visible_to" - ] + fields = ["type", "visible_to"] class CommentSerializer(serializers.ModelSerializer): id = serializers.UUIDField(read_only=True) - creator = serializers.SerializerMethodField('get_creator') + creator = serializers.SerializerMethodField("get_creator") created_on = serializers.DateTimeField(read_only=True) publish = PublishingRuleSerializer(read_only=True) @@ -46,7 +48,7 @@ def save(self, **kwargs): "method": request.method, "REMOTE_ADDR": request.META["REMOTE_ADDR"], "HTTP_USER_AGENT": request.META["HTTP_USER_AGENT"], - "lang": request.LANGUAGE_CODE + "lang": request.LANGUAGE_CODE, } filter_spam_task.delay(ctx) @@ -54,63 +56,51 @@ def save(self, **kwargs): class Meta: model = Comment - fields = [ - "id", - "creator", - "text", - "created_on", - "publish" - ] + fields = ["id", "creator", "text", "created_on", "publish"] def get_creator(self, obj): - return {"id": str(obj.creator.id), "username": obj.creator.username, "avatar": obj.creator.avatar} + return { + "id": str(obj.creator.id), + "username": obj.creator.username, + "avatar": obj.creator.avatar, + } class ImageSerializer(serializers.ModelSerializer): - class Meta: model = Image - fields = [ - "image_url", - "public_id" - ] + fields = ["image_url", "public_id"] class TagSerializer(serializers.ModelSerializer): - class Meta: model = Tag - fields = [ - "name" - ] + fields = ["name"] class CategorySerializer(serializers.ModelSerializer): class Meta: model = Category - fields = [ - "id", - "name" - ] + fields = ["id", "name"] class ProjectSerializer(serializers.ModelSerializer): creator = CreatorMinimalSerializer(read_only=True) - likes = serializers.SlugRelatedField( - many=True, slug_field='id', read_only=True) - saved_by = serializers.SlugRelatedField( - many=True, slug_field='id', read_only=True) - comments = serializers.SerializerMethodField('get_comments') + likes = serializers.SlugRelatedField(many=True, slug_field="id", read_only=True) + saved_by = serializers.SlugRelatedField(many=True, slug_field="id", read_only=True) + comments = serializers.SerializerMethodField("get_comments") images = ImageSerializer(many=True, required=False) tags = TagSerializer(many=True, required=False, read_only=True) category = serializers.SlugRelatedField( - slug_field="name", queryset=Category.objects.all(), required=False,many=True) + slug_field="name", queryset=Category.objects.all(), required=False, many=True + ) created_on = serializers.DateTimeField(read_only=True) views_count = serializers.IntegerField(read_only=True) publish = PublishingRuleSerializer(read_only=True) activity = serializers.SlugRelatedField( - slug_field="id", queryset=Activity.objects.all(), required=False) - group = CreatorGroupSerializer(read_only = True) + slug_field="id", queryset=Activity.objects.all(), required=False + ) + group = CreatorGroupSerializer(read_only=True) class Meta: model = Project @@ -131,7 +121,7 @@ class Meta: "created_on", "publish", "activity", - "group" + "group", ] read_only_fields = ["created_on", "views_count"] @@ -150,64 +140,68 @@ def get_comments(self, obj): for comment in all_comments: creators_dict[comment["creator"]["id"]] = comment["creator"] - root_comments = list( - map(lambda x: Comment.dump_bulk(x)[0], root_comments)) + root_comments = list(map(lambda x: Comment.dump_bulk(x)[0], root_comments)) user = self.context.get("request").user return parse_comment_trees(user, root_comments, creators_dict) def validate_video(self, video): - if(self.initial_data.get('publish').get('type') == 1): + if self.initial_data.get("publish").get("type") == 1: return video - - if(video == "" and len(self.initial_data.get("images")) == 0): + + if video == "" and len(self.initial_data.get("images")) == 0: raise serializers.ValidationError( - _("You must provide either image(s), video file, or video URL to create your project!")) + _( + "You must provide either image(s), " + "video file, or video URL to create your project!" + ) + ) return video def validate_images(self, images): - if(self.initial_data.get('publish').get('type') == 1): + if self.initial_data.get("publish").get("type") == 1: return images - - if(len(images) == 0 and len(self.initial_data["video"]) == 0): + + if len(images) == 0 and len(self.initial_data["video"]) == 0: raise serializers.ValidationError( - _("You must provide either image(s), video file, or video URL to create your project!")) + _( + "You must provide either image(s), " + "video file, or video URL to create your project!" + ) + ) return images def validate_tags(self, tags): if not isinstance(tags, list): - raise serializers.ValidationError( - _("tags format not supported") - ) + raise serializers.ValidationError(_("tags format not supported")) try: if len(tags) > 0: # attempt parsing tags list(map(lambda x: x["name"], tags)) - except: - raise serializers.ValidationError( - _("tags format not supported") - ) + except: # noqa: E722 + raise serializers.ValidationError(_("tags format not supported")) for tag in tags: - if (not re.fullmatch("[0-9A-Za-z\s\-]+", tag["name"])) or (tag["name"] == " "): + if (not re.fullmatch("[0-9A-Za-z\s\-]+", tag["name"])) or ( # noqa: W605 + tag["name"] == " " + ): raise serializers.ValidationError( _("tags support only letters, numbers, spaces, and dashes") ) if len(tags) > 5: - raise serializers.ValidationError( - _("tags must not be more than 5") - ) + raise serializers.ValidationError(_("tags must not be more than 5")) return tags def validate_publish(self, publish): - try: - if publish["type"] not in [ - PublishingRule.DRAFT, PublishingRule.PUBLIC, - PublishingRule.AUTHENTICATED_VIEWERS, PublishingRule.PREVIEW]: + PublishingRule.DRAFT, + PublishingRule.PUBLIC, + PublishingRule.AUTHENTICATED_VIEWERS, + PublishingRule.PREVIEW, + ]: raise serializers.ValidationError( _("publish type is not supported. must be one of 1,2,3 or 4") ) @@ -217,35 +211,38 @@ def validate_publish(self, publish): _("publish visible_to must be a list") ) - if (publish["type"] == PublishingRule.PREVIEW and - len(publish["visible_to"]) == 0): + if ( + publish["type"] == PublishingRule.PREVIEW + and len(publish["visible_to"]) == 0 + ): raise serializers.ValidationError( - _("publish visible_to must not be empty when publish type is Preview") + _( + "publish visible_to must not be empty " + "when publish type is Preview" + ) ) - except: - raise serializers.ValidationError( - _("publish format is not supported") - ) + except: # noqa: E722 + raise serializers.ValidationError(_("publish format is not supported")) return publish def create(self, validated_data): - images_data = validated_data.pop('images') - tags_data = self.validate_tags( - self.context['request'].data.get("tags", [])) - category = validated_data.pop('category') - activity = validated_data.pop('activity', None) + images_data = validated_data.pop("images") + tags_data = self.validate_tags(self.context["request"].data.get("tags", [])) + category = validated_data.pop("category") + activity = validated_data.pop("activity", None) - publish = self.validate_publish( - self.context['request'].data.get("publish", {})) + publish = self.validate_publish(self.context["request"].data.get("publish", {})) with transaction.atomic(): - rule = PublishingRule.objects.create(type=publish["type"], - publisher_id=str(self.context["request"].user.id)) + rule = PublishingRule.objects.create( + type=publish["type"], publisher_id=str(self.context["request"].user.id) + ) if rule.type == PublishingRule.PREVIEW: - rule.visible_to.set(Creator.objects.filter( - username__in=publish["visible_to"])) + rule.visible_to.set( + Creator.objects.filter(username__in=publish["visible_to"]) + ) project = Project.objects.create(**validated_data, publish=rule) @@ -262,38 +259,48 @@ def create(self, validated_data): if activity is not None: activity.inspired_projects.add(project) - if project.video.find("cloudinary.com") > -1 and project.video.split(".")[-1] != "mpd": + if ( + project.video.find("cloudinary.com") > -1 + and project.video.split(".")[-1] != "mpd" + ): update_video_url_if_transform_ready.delay( - {"url": project.video, "project_id": project.id}) - + {"url": project.video, "project_id": project.id} + ) + return project def update(self, project, validated_data): - images_data = validated_data.pop('images') + images_data = validated_data.pop("images") tags_data = self.validate_tags(self.initial_data.get("tags", [])) publish = self.validate_publish(self.initial_data.get("publish", {})) category = None - if validated_data.get('category', []): - category = validated_data.pop('category') - + if validated_data.get("category", []): + category = validated_data.pop("category") + video_data = validated_data.pop("video") - if (project.video.find("cloudinary.com") > -1 or - project.video.startswith("{0}://{1}".format( - settings.DEFAULT_MEDIA_SERVER_PROTOCOL, - settings.DEFAULT_MEDIA_SERVER_DOMAIN)) and - project.video != video_data): + if ( + project.video.find("cloudinary.com") > -1 + or project.video.startswith( + "{0}://{1}".format( + settings.DEFAULT_MEDIA_SERVER_PROTOCOL, + settings.DEFAULT_MEDIA_SERVER_DOMAIN, + ) + ) + and project.video != video_data + ): delete_file_task.delay(project.video) with transaction.atomic(): - - rule = PublishingRule.objects.create(type=publish["type"], - publisher_id=str(self.context["request"].user.id)) + rule = PublishingRule.objects.create( + type=publish["type"], publisher_id=str(self.context["request"].user.id) + ) if rule.type == PublishingRule.PREVIEW: - rule.visible_to.set(Creator.objects.filter( - username__in=publish["visible_to"])) + rule.visible_to.set( + Creator.objects.filter(username__in=publish["visible_to"]) + ) project.title = validated_data.pop("title") project.description = validated_data.pop("description") @@ -308,25 +315,31 @@ def update(self, project, validated_data): if category: project.category.set(category) - else: project.category.set([]) + else: + project.category.set([]) - if project.video.find("cloudinary.com") > -1 and project.video.split(".")[-1] != "mpd": + if ( + project.video.find("cloudinary.com") > -1 + and project.video.split(".")[-1] != "mpd" + ): update_video_url_if_transform_ready.delay( - {"url": project.video, "project_id": project.id}) + {"url": project.video, "project_id": project.id} + ) return project class ProjectListSerializer(serializers.ModelSerializer): creator = CreatorMinimalSerializer(read_only=True) - group = CreatorGroupSerializer(read_only = True) + group = CreatorGroupSerializer(read_only=True) images = ImageSerializer(many=True) - likes = serializers.SlugRelatedField( - many=True, slug_field='id', read_only=True) - saved_by = serializers.SlugRelatedField( - many=True, slug_field='id', read_only=True) + likes = serializers.SlugRelatedField(many=True, slug_field="id", read_only=True) + saved_by = serializers.SlugRelatedField(many=True, slug_field="id", read_only=True) created_on = serializers.DateTimeField(read_only=True) publish = PublishingRuleSerializer(read_only=True) + activity = serializers.SlugRelatedField( + slug_field="id", queryset=Activity.objects.all(), required=False + ) class Meta: model = Project @@ -343,39 +356,42 @@ class Meta: "comments_count", "created_on", "publish", - "group" + "group", + "activity", ] class StaffPickSerializer(serializers.ModelSerializer): - projects = serializers.SerializerMethodField('paginated_projects') + projects = serializers.SerializerMethodField("paginated_projects") class Meta: model = StaffPick fields = [ - 'id', - 'title', - 'description', - 'projects', - 'created_on', + "id", + "title", + "description", + "projects", + "created_on", ] def paginated_projects(self, obj): projects = obj.projects.all() projects = get_published_projects_for_user( - self.context['request'].user, - projects) + self.context["request"].user, projects + ) paginator = ProjectNumberPagination() - page = paginator.paginate_queryset( - projects, self.context['request']) - serializer = ProjectSerializer(page, read_only=True, many=True, context={ - 'request': self.context['request']}) + page = paginator.paginate_queryset(projects, self.context["request"]) + serializer = ProjectSerializer( + page, + read_only=True, + many=True, + context={"request": self.context["request"]}, + ) count = projects.count() - num_pages = ceil(count/paginator.page_size) - current_page = int( - self.context["request"].query_params.get("page", "1")) + num_pages = ceil(count / paginator.page_size) + current_page = int(self.context["request"].query_params.get("page", "1")) if current_page != 1: prev_page = current_page - 1 else: @@ -386,4 +402,9 @@ def paginated_projects(self, obj): else: next_page = None - return {"results": serializer.data, "prev": prev_page, "next": next_page, "count": count} + return { + "results": serializer.data, + "prev": prev_page, + "next": next_page, + "count": count, + } diff --git a/zubhub_frontend/zubhub/public/locales/en/translation.json b/zubhub_frontend/zubhub/public/locales/en/translation.json index 98319955..0c65bf3e 100644 --- a/zubhub_frontend/zubhub/public/locales/en/translation.json +++ b/zubhub_frontend/zubhub/public/locales/en/translation.json @@ -348,8 +348,8 @@ "unexpected": "Uh oh! Seems like we hit a snag :( Maybe try again later?" } }, - "createProject": { + "activityProjectCreate": "Create this Project", "welcomeMsg": { "primary": "Create Project", "secondary": "Tell us about your project!" diff --git a/zubhub_frontend/zubhub/public/locales/hi/translation.json b/zubhub_frontend/zubhub/public/locales/hi/translation.json index 9ba8ec90..2a6f4536 100644 --- a/zubhub_frontend/zubhub/public/locales/hi/translation.json +++ b/zubhub_frontend/zubhub/public/locales/hi/translation.json @@ -299,6 +299,7 @@ }, "createProject": { + "activityProjectCreate": "यह प्रोजेक्ट बनाएं", "welcomeMsg": { "primary": "प्रोजेक्ट बनाएं", "secondary": "हमें अपनी परियोजना के बारे में बताएं!" diff --git a/zubhub_frontend/zubhub/src/views/activity_details/ActivityDetailsV2.jsx b/zubhub_frontend/zubhub/src/views/activity_details/ActivityDetailsV2.jsx index 63603322..ada50980 100644 --- a/zubhub_frontend/zubhub/src/views/activity_details/ActivityDetailsV2.jsx +++ b/zubhub_frontend/zubhub/src/views/activity_details/ActivityDetailsV2.jsx @@ -87,6 +87,10 @@ export default function ActivityDetailsV2(props) { props.navigate(`${props.location.pathname}/edit`); }; + const handleCreateProject = () => { + props.history.push('/projects/create', { activity_id: activity.id }); + }; + const handleDownload = useReactToPrint({ onBeforePrint: () => setIsDownloading(true), onPrintError: () => setIsDownloading(false), @@ -140,7 +144,7 @@ export default function ActivityDetailsV2(props) {
- + Create this Project