Exemple #1
0
class SimpleActivitySerializer(serializers.ModelSerializer):
    action = serializers.CharField(source='verb')
    activity_type = serializers.SerializerMethodField()
    activity = GenericRelatedField(
        {
            get_user_model(): SimpleUserSerializer(),
            Channel: ChannelSerializer(),
            ChannelUser: ChannelUserSerializer(),
            Message: MessageSerializer(),
            Comment: CommentSerializer(),
            Upload: UploadSerializer(),
            Connection: ConnectionSerializer(),
            Task: SimpleTaskSerializer(),
            Application: ApplicationSerializer(),
            Participation: ParticipationSerializer(),
            Estimate: SimpleEstimateSerializer(),
            Quote: SimpleQuoteSerializer(),
            ProgressEvent: SimpleProgressEventSerializer(),
            ProgressReport: ProgressReportSerializer(),
            Integration: SimpleIntegrationSerializer(),
            IntegrationActivity: SimpleIntegrationActivitySerializer()
        },
        source='action_object')

    class Meta:
        model = Action
        exclude = ('verb', 'actor_object_id', 'actor_content_type',
                   'action_object_object_id', 'action_object_content_type',
                   'target_object_id', 'target_content_type')

    def get_activity_type(self, obj):
        return get_instance_type(obj.action_object)
Exemple #2
0
class CommentSerializer(serializers.ModelSerializer):
    user = SimpleUserSerializer(required=False,
                                read_only=True,
                                default=CreateOnlyCurrentUserDefault())
    uploads = UploadSerializer(read_only=True, required=False, many=True)

    class Meta:
        model = Comment
        read_only_fields = ('created_at', )
class CommentSerializer(serializers.ModelSerializer):
    user = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault())
    uploads = UploadSerializer(read_only=True, required=False, many=True)
    text_body = serializers.CharField(required=False, read_only=True)
    html_body = serializers.CharField(required=False, read_only=True)

    class Meta:
        model = Comment
        fields = '__all__'
        read_only_fields = ('created_at',)
class MessageSerializer(serializers.ModelSerializer, GetCurrentUserAnnotatedSerializerMixin):
    user = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault())
    excerpt = serializers.CharField(required=False, read_only=True)
    attachments = UploadSerializer(read_only=True, required=False, many=True)
    sender = SenderSerializer(read_only=True, required=False)
    text_body = serializers.CharField(required=False, read_only=True)
    html_body = serializers.CharField(required=False, read_only=True)

    class Meta:
        model = Message
        exclude = ('alt_user', 'source', 'extra')
        read_only_fields = ('created_at',)
Exemple #5
0
class ProgressReportSerializer(ContentTypeAnnotatedModelSerializer,
                               DetailAnnotatedModelSerializer):
    user = serializers.PrimaryKeyRelatedField(
        required=False, read_only=True, default=CreateOnlyCurrentUserDefault())
    status_display = serializers.CharField(required=False,
                                           read_only=True,
                                           source='get_status_display')
    uploads = UploadSerializer(required=False, read_only=True, many=True)

    class Meta:
        model = ProgressReport
        read_only_fields = ('created_at', )
        details_serializer = ProgressReportDetailsSerializer
class ActivitySerializer(SimpleActivitySerializer):
    actor_type = serializers.SerializerMethodField()
    target_type = serializers.SerializerMethodField()
    actor = GenericRelatedField({
        get_user_model(): SimpleUserSerializer(),
        Integration: SimpleIntegrationSerializer()
    })
    target = GenericRelatedField({
        get_user_model():
        SimpleUserSerializer(),
        Channel:
        ChannelSerializer(),
        ChannelUser:
        ChannelUserSerializer(),
        Message:
        MessageSerializer(),
        Comment:
        CommentSerializer(),
        Upload:
        UploadSerializer(),
        Connection:
        ConnectionSerializer(),
        Task:
        SimpleTaskSerializer(),
        Application:
        ApplicationSerializer(),
        Participation:
        ParticipationSerializer(),
        ProgressEvent:
        SimpleProgressEventSerializer(),
        ProgressReport:
        SimpleProgressReportSerializer(),
        Integration:
        SimpleIntegrationSerializer(),
        IntegrationActivity:
        SimpleIntegrationActivitySerializer()
    })

    class Meta(SimpleActivitySerializer.Meta):
        fields = '__all__'
        exclude = None

    def get_actor_type(self, obj):
        return get_instance_type(obj.actor)

    def get_target_type(self, obj):
        return get_instance_type(obj.target)
Exemple #7
0
class SimpleProgressReportSerializer(BasicProgressReportSerializer):
    uploads = UploadSerializer(required=False, read_only=True, many=True)

    class Meta:
        model = ProgressReport
Exemple #8
0
class TaskSerializer(ContentTypeAnnotatedModelSerializer,
                     DetailAnnotatedModelSerializer,
                     GetCurrentUserAnnotatedSerializerMixin):
    user = serializers.PrimaryKeyRelatedField(
        required=False, read_only=True, default=CreateOnlyCurrentUserDefault())
    display_fee = serializers.SerializerMethodField(required=False,
                                                    read_only=True)
    excerpt = serializers.CharField(required=False, read_only=True)
    skills = serializers.CharField(required=True,
                                   allow_blank=True,
                                   allow_null=True)
    deadline = serializers.DateTimeField(required=False, allow_null=True)
    can_apply = serializers.SerializerMethodField(read_only=True,
                                                  required=False)
    can_save = serializers.SerializerMethodField(read_only=True,
                                                 required=False)
    is_participant = serializers.SerializerMethodField(read_only=True,
                                                       required=False)
    my_participation = serializers.SerializerMethodField(read_only=True,
                                                         required=False)
    summary = serializers.CharField(read_only=True, required=False)
    assignee = BasicParticipationSerializer(required=False, read_only=True)
    participants = serializers.PrimaryKeyRelatedField(
        many=True,
        queryset=get_user_model().objects.all(),
        required=False,
        write_only=True)
    open_applications = serializers.SerializerMethodField(required=False,
                                                          read_only=True)
    update_schedule_display = serializers.CharField(required=False,
                                                    read_only=True)
    participation = NestedTaskParticipationSerializer(required=False,
                                                      read_only=False,
                                                      many=True)
    milestones = NestedProgressEventSerializer(required=False,
                                               read_only=False,
                                               many=True)
    progress_events = NestedProgressEventSerializer(required=False,
                                                    read_only=True,
                                                    many=True)
    ratings = SimpleRatingSerializer(required=False,
                                     read_only=False,
                                     many=True)
    uploads = UploadSerializer(required=False, read_only=True, many=True)
    all_uploads = UploadSerializer(required=False, read_only=True, many=True)

    class Meta:
        model = Task
        exclude = ('applicants', )
        read_only_fields = ('created_at', )
        details_serializer = TaskDetailsSerializer

    def create(self, validated_data):
        skills = None
        participation = None
        milestones = None
        participants = None
        ratings = None
        if 'skills' in validated_data:
            skills = validated_data.pop('skills')
        if 'participation' in validated_data:
            participation = validated_data.pop('participation')
        if 'milestones' in validated_data:
            milestones = validated_data.pop('milestones')
        if 'participants' in validated_data:
            participants = validated_data.pop('participants')
        if 'ratings' in validated_data:
            ratings = validated_data.pop('ratings')

        if participation or participants:
            # close applications if paticipants are provided
            validated_data['apply'] = False
        instance = super(TaskSerializer, self).create(validated_data)
        self.save_skills(instance, skills)
        self.save_participants(instance, participants)
        self.save_participation(instance, participation)
        self.save_milestones(instance, milestones)
        self.save_ratings(instance, ratings)

        # Triggered here instead of in the post_save signal to allow skills to be attached first
        # TODO: Consider moving this trigger
        send_new_task_email.delay(instance.id)
        return instance

    def update(self, instance, validated_data):
        initial_apply = instance.apply
        initial_closed = instance.closed

        skills = None
        participation = None
        milestones = None
        participants = None
        ratings = None
        if 'skills' in validated_data:
            skills = validated_data.pop('skills')
        if 'participation' in validated_data:
            participation = validated_data.pop('participation')
        if 'milestones' in validated_data:
            milestones = validated_data.pop('milestones')
        if 'participants' in validated_data:
            participants = validated_data.pop('participants')
        if 'ratings' in validated_data:
            ratings = validated_data.pop('ratings')

        if not instance.closed and validated_data.get('closed'):
            validated_data['closed_at'] = datetime.datetime.utcnow()

        if not instance.paid and validated_data.get('paid'):
            validated_data['paid_at'] = datetime.datetime.utcnow()

        instance = super(TaskSerializer, self).update(instance, validated_data)
        self.save_skills(instance, skills)
        self.save_participants(instance, participants)
        self.save_participation(instance, participation)
        self.save_milestones(instance, milestones)
        self.save_ratings(instance, ratings)

        if initial_apply and not instance.apply:
            task_applications_closed.send(sender=Task, task=instance)

        if not initial_closed and instance.closed:
            task_closed.send(sender=Task, task=instance)
        return instance

    def save_skills(self, task, skills):
        if skills is not None:
            task.skills = skills
            task.save()

    def save_participation(self, task, participation):
        if participation:
            new_assignee = None
            for item in participation:
                if item.get('accepted', False):
                    item['activated_at'] = datetime.datetime.utcnow()
                try:
                    Participation.objects.update_or_create(task=task,
                                                           user=item['user'],
                                                           defaults=item)
                    if 'assignee' in item and item['assignee']:
                        new_assignee = item['user']
                except:
                    pass
            if new_assignee:
                Participation.objects.exclude(user=new_assignee).filter(
                    task=task).update(assignee=False)

    def save_milestones(self, task, milestones):
        if milestones:
            for item in milestones:
                event_type = item.get('type', PROGRESS_EVENT_TYPE_MILESTONE)
                if event_type != PROGRESS_EVENT_TYPE_MILESTONE:
                    continue
                defaults = {'created_by': self.get_current_user() or task.user}
                defaults.update(item)
                try:
                    ProgressEvent.objects.update_or_create(
                        task=task,
                        type=event_type,
                        due_at=item['due_at'],
                        defaults=defaults)
                except:
                    pass

    def save_ratings(self, task, ratings):
        if ratings:
            for item in ratings:
                try:
                    Rating.objects.update_or_create(
                        content_type=ContentType.objects.get_for_model(task),
                        object_id=task.id,
                        criteria=item['criteria'],
                        defaults=item)
                except:
                    pass

    def save_participants(self, task, participants):
        # TODO: Remove and move existing code to using save_participation
        if participants:
            assignee = self.initial_data.get('assignee', None)
            confirmed_participants = self.initial_data.get(
                'confirmed_participants', None)
            rejected_participants = self.initial_data.get(
                'rejected_participants', None)
            created_by = self.get_current_user()
            if not created_by:
                created_by = task.user

            changed_assignee = False
            for user in participants:
                try:
                    defaults = {'created_by': created_by}
                    if assignee:
                        defaults['assignee'] = bool(user.id == assignee)
                    if rejected_participants and user.id in rejected_participants:
                        defaults['accepted'] = False
                        defaults['responded'] = True
                    if confirmed_participants and user.id in confirmed_participants:
                        defaults['accepted'] = True
                        defaults['responded'] = True
                        defaults['activated_at'] = datetime.datetime.utcnow()

                    Participation.objects.update_or_create(task=task,
                                                           user=user,
                                                           defaults=defaults)
                    if user.id == assignee:
                        changed_assignee = True
                except:
                    pass
            if assignee and changed_assignee:
                Participation.objects.exclude(user__id=assignee).filter(
                    task=task).update(assignee=False)

    def get_display_fee(self, obj):
        user = self.get_current_user()
        amount = None
        if user and user.is_developer:
            amount = obj.fee * (1 - TUNGA_SHARE_PERCENTAGE * 0.01)
        return obj.display_fee(amount=amount)

    def get_can_apply(self, obj):
        if obj.closed or not obj.apply:
            return False
        user = self.get_current_user()
        if user:
            if obj.user == user or user.pending:
                return False
            return obj.applicants.filter(id=user.id).count() == 0 and \
                   obj.participation_set.filter(user=user).count() == 0
        return False

    def get_can_save(self, obj):
        user = self.get_current_user()
        if user:
            if obj.user == user:
                return False
            return obj.savedtask_set.filter(user=user).count() == 0
        return False

    def get_is_participant(self, obj):
        user = self.get_current_user()
        if user:
            return obj.participation_set.filter(
                (Q(accepted=True) | Q(responded=False)),
                user=user).count() == 1
        return False

    def get_my_participation(self, obj):
        user = self.get_current_user()
        if user:
            try:
                participation = obj.participation_set.get(user=user)
                return {
                    'id': participation.id,
                    'user': participation.user.id,
                    'assignee': participation.assignee,
                    'accepted': participation.accepted,
                    'responded': participation.responded
                }
            except:
                pass
        return None

    def get_open_applications(self, obj):
        return obj.application_set.filter(responded=False).count()
Exemple #9
0
class ChannelSerializer(DetailAnnotatedModelSerializer,
                        GetCurrentUserAnnotatedSerializerMixin):
    created_by = SimpleUserSerializer(required=False,
                                      read_only=True,
                                      default=CreateOnlyCurrentUserDefault())
    display_type = serializers.CharField(required=False,
                                         read_only=True,
                                         source='get_type_display')
    participants = serializers.PrimaryKeyRelatedField(
        required=True, many=True, queryset=get_user_model().objects.all())
    attachments = UploadSerializer(read_only=True,
                                   required=False,
                                   many=True,
                                   source='all_attachments')
    user = serializers.SerializerMethodField(read_only=True, required=False)
    new_messages = serializers.IntegerField(read_only=True, required=False)
    new = serializers.SerializerMethodField(read_only=True, required=False)
    last_read = serializers.SerializerMethodField(read_only=True,
                                                  required=False)
    alt_subject = serializers.CharField(required=False,
                                        read_only=True,
                                        source='get_alt_subject')

    class Meta:
        model = Channel
        exclude = ()
        read_only_fields = ('created_at', 'type')
        details_serializer = ChannelDetailsSerializer

    def validate_participants(self, value):
        error = 'Select some participants for this conversation'
        if not isinstance(value, list) or not value:
            raise ValidationError(error)
        participants = self.clean_participants(value)
        if not participants:
            raise ValidationError(error)
        return value

    def create(self, validated_data):
        participants = None
        if 'participants' in validated_data:
            participants = validated_data.pop('participants')
        participants = self.clean_participants(participants)
        subject = validated_data.get('subject', None)
        if not subject and isinstance(participants,
                                      list) and len(participants) == 1:
            # Create or get a direct channel
            # if only one other participant is given and no subject is stated for the communication
            current_user = self.get_current_user()
            channel = get_or_create_direct_channel(current_user,
                                                   participants[0])
        else:
            #if not subject:
            #    raise ValidationError({'subject': 'Enter a subject for this conversation'})
            channel = Channel.objects.create(**validated_data)
        self.save_participants(channel, participants)
        return channel

    def update(self, instance, validated_data):
        participants = None
        if 'participants' in validated_data:
            participants = validated_data.pop('participants')
        participants = self.clean_participants(participants)
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        subject = validated_data.get('subject', None)
        if not subject and isinstance(participants,
                                      list) and len(participants) > 1:
            raise ValidationError(
                {'subject': 'Enter a subject for this conversation'})
        instance.save()
        self.save_participants(instance, participants)
        return instance

    def clean_participants(self, participants):
        current_user = self.get_current_user()
        if isinstance(participants, (list, tuple)) and current_user:
            return [
                user_id for user_id in participants
                if user_id != current_user.id
            ]
        return participants

    def save_participants(self, instance, participants):
        if participants:
            participants.append(instance.created_by)
            for user in participants:
                try:
                    ChannelUser.objects.update_or_create(channel=instance,
                                                         user=user)
                except:
                    pass

    def get_user(self, obj):
        current_user = self.get_current_user()
        if current_user:
            receiver = obj.get_receiver(current_user)
            if receiver:
                return SimpleUserSerializer(receiver).data
        return None

    def get_new(self, obj):
        user = self.get_current_user()
        if user:
            return channel_activity_new_messages_filter(
                queryset=obj.target_actions.filter(
                    channels__channeluser__user=user),
                user=user).count()
        return 0

    def get_last_read(self, obj):
        user = self.get_current_user()
        if user:
            try:
                return obj.channeluser_set.get(user=user).last_read
            except:
                pass
        else:
            return obj.last_read
        return 0
Exemple #10
0
 def get_uploads(self, obj):
     content_type = ContentType.objects.get_for_model(Comment)
     uploads = Upload.objects.filter(content_type=content_type,
                                     object_id=obj.id)
     return UploadSerializer(uploads, many=True).data
Exemple #11
0
class TaskSerializer(ContentTypeAnnotatedModelSerializer,
                     DetailAnnotatedModelSerializer,
                     GetCurrentUserAnnotatedSerializerMixin):
    user = SimpleUserSerializer(required=False,
                                read_only=True,
                                default=CreateOnlyCurrentUserDefault())
    pay = serializers.DecimalField(max_digits=19,
                                   decimal_places=4,
                                   required=False,
                                   read_only=True)
    display_fee = serializers.SerializerMethodField(required=False,
                                                    read_only=True)
    amount = serializers.JSONField(required=False, read_only=True)
    is_payable = serializers.BooleanField(required=False, read_only=True)
    is_project = serializers.BooleanField(required=False, read_only=True)
    is_task = serializers.BooleanField(required=False, read_only=True)
    is_developer_ready = serializers.BooleanField(required=False,
                                                  read_only=True)
    requires_estimate = serializers.BooleanField(required=False,
                                                 read_only=True)
    excerpt = serializers.CharField(required=False, read_only=True)
    skills = serializers.CharField(
        required=False,
        error_messages={
            'blank': 'Please specify the skills required for this task'
        })
    payment_status = serializers.CharField(required=False, read_only=True)
    deadline = serializers.DateTimeField(required=False, allow_null=True)
    can_apply = serializers.SerializerMethodField(read_only=True,
                                                  required=False)
    can_claim = serializers.SerializerMethodField(read_only=True,
                                                  required=False)
    can_return = serializers.SerializerMethodField(read_only=True,
                                                   required=False)
    can_save = serializers.SerializerMethodField(read_only=True,
                                                 required=False)
    is_participant = serializers.SerializerMethodField(read_only=True,
                                                       required=False)
    is_admin = serializers.SerializerMethodField(read_only=True,
                                                 required=False)
    my_participation = serializers.SerializerMethodField(read_only=True,
                                                         required=False)
    summary = serializers.CharField(read_only=True, required=False)
    assignee = SimpleParticipationSerializer(required=False, read_only=True)
    participants = serializers.PrimaryKeyRelatedField(
        many=True,
        queryset=get_user_model().objects.all(),
        required=False,
        write_only=True)
    update_schedule_display = serializers.CharField(required=False,
                                                    read_only=True)
    participation = NestedTaskParticipationSerializer(required=False,
                                                      read_only=False,
                                                      many=True)
    milestones = NestedProgressEventSerializer(required=False,
                                               read_only=False,
                                               many=True)
    progress_events = NestedProgressEventSerializer(required=False,
                                                    read_only=True,
                                                    many=True)
    ratings = SimpleRatingSerializer(required=False,
                                     read_only=False,
                                     many=True)
    uploads = UploadSerializer(required=False, read_only=True, many=True)
    all_uploads = UploadSerializer(required=False, read_only=True, many=True)
    invoice = TaskInvoiceSerializer(required=False, read_only=True)
    estimate = SimpleEstimateSerializer(required=False, read_only=True)
    quote = SimpleQuoteSerializer(required=False, read_only=True)
    pm = SimpleUserSerializer(required=False, read_only=True)

    class Meta:
        model = Task
        exclude = ('applicants', )
        read_only_fields = ('created_at', 'paid', 'paid_at', 'invoice_date',
                            'btc_address', 'btc_price', 'pay_distributed')
        extra_kwargs = {
            'type': {
                'required': True,
                'allow_blank': False,
                'allow_null': False
            },
            'scope': {
                'required': True,
                'allow_blank': False,
                'allow_null': False
            }
        }
        details_serializer = TaskDetailsSerializer

    def validate(self, attrs):
        has_parent = attrs.get('parent', None) or (self.instance
                                                   and self.instance.parent)
        scope = attrs.get('scope', None) or (self.instance
                                             and self.instance.scope)
        is_project = attrs.get(
            'is_project', None) or (self.instance and self.instance.is_project)
        has_requirements = attrs.get(
            'is_project', None) or (self.instance
                                    and self.instance.has_requirements)
        coders_needed = attrs.get('coders_needed', None)
        pm_required = attrs.get('pm_required', None)

        fee = attrs.get('fee', None)
        title = attrs.get('title', None)
        skills = attrs.get('skills', None)
        visibility = attrs.get('visibility', None)

        description = attrs.get('description', None)
        email = self.initial_data.get('email', None)
        first_name = self.initial_data.get('first_name', None)
        last_name = self.initial_data.get('last_name', None)

        current_user = self.get_current_user()

        errors = dict()

        if current_user and current_user.is_authenticated():
            if scope == TASK_SCOPE_TASK or has_parent:
                if not has_parent and fee:
                    MinValueValidator(
                        15, message='Minimum pledge amount is EUR 15')(fee)
                if not title and not self.partial:
                    errors.update({'title': 'This field is required.'})
                if not description and not self.partial:
                    errors.update({'description': 'This field is required.'})
                if not skills and not self.partial:
                    errors.update({'skills': 'This field is required.'})
                if visibility == VISIBILITY_CUSTOM and not (
                        attrs.get('participation', None)
                        or attrs.get('participants', None)):
                    errors.update({
                        'visibility':
                        'Please choose at least one developer for this task'
                    })
                if scope == TASK_SCOPE_TASK and description and self.partial and len(
                        description.split(' ')) <= 15:
                    errors.update({
                        'description':
                        'Please provide a more detailed description.'
                    })

            if scope == TASK_SCOPE_ONGOING:
                if not skills and not self.partial:
                    errors.update({'skills': 'This field is required.'})
                if not coders_needed and not self.partial:
                    errors.update({'coders_needed': 'This field is required.'})
            if scope == TASK_SCOPE_PROJECT:
                if not title and not self.partial:
                    errors.update({'title': 'This field is required.'})
                if not skills and not self.partial:
                    errors.update({'skills': 'This field is required.'})
                if not pm_required and not self.partial:
                    errors.update({'pm_required': 'This field is required.'})
        else:
            if not description:
                errors.update({'description': 'This field is required.'})
            if email:
                try:
                    get_user_model().objects.get(email=email)
                    errors.update({
                        'form':
                        'Looks like you already have a Tunga account. Please login to create new tasks.',
                        'email':
                        'This email address is already attached to an account on Tunga'
                    })
                except get_user_model().DoesNotExist:
                    pass
            else:
                errors.update({'email': 'This field is required.'})
            if not first_name:
                errors.update({'first_name': 'This field is required.'})
            if not last_name:
                errors.update({'last_name': 'This field is required.'})

        if errors:
            raise ValidationError(errors)

        return attrs

    def save_task(self, validated_data, instance=None):
        current_user = self.get_current_user()
        if current_user and current_user.is_authenticated(
        ) and not instance and not profile_check(current_user):
            ValidationError(
                'You need complete your profile before you can post tasks')

        if instance and 'fee' in validated_data and validated_data[
                'fee'] < instance.fee:
            raise ValidationError({
                'fee':
                'You cannot reduce the fee for the task, Please contact [email protected] for assistance'
            })

        skills = None
        participation = None
        milestones = None
        participants = None
        ratings = None
        if 'skills' in validated_data:
            skills = validated_data.pop('skills')
        if 'participation' in validated_data:
            participation = validated_data.pop('participation')
        if 'milestones' in validated_data:
            milestones = validated_data.pop('milestones')
        if 'participants' in validated_data:
            participants = validated_data.pop('participants')
        if 'ratings' in validated_data:
            ratings = validated_data.pop('ratings')

        initial_apply = True
        initial_closed = False
        initial_approved = False
        new_user = None
        is_update = bool(instance)

        if instance:
            initial_apply = instance.apply
            initial_closed = instance.closed
            initial_approved = instance.approved

            if not instance.closed and validated_data.get('closed'):
                validated_data['closed_at'] = datetime.datetime.utcnow()

            if not instance.paid and validated_data.get('paid'):
                validated_data['paid_at'] = datetime.datetime.utcnow()
            instance = super(TaskSerializer,
                             self).update(instance, validated_data)
        else:
            if not current_user or not current_user.is_authenticated():
                validated_data['source'] = TASK_SOURCE_NEW_USER
            if participation or participants:
                # Close applications if paticipants are provided when creating task
                validated_data['apply'] = False
                validated_data['apply_closed_at'] = datetime.datetime.utcnow()

            if not current_user or not current_user.is_authenticated():
                # Create user and add them as the creator or task, indicate if task was unauthenticated
                email = self.initial_data.get('email', None)
                first_name = self.initial_data.get('first_name', None)
                last_name = self.initial_data.get('last_name', None)

                new_user = get_user_model().objects.create_user(
                    username=email,
                    email=email,
                    password=get_user_model().objects.make_random_password(),
                    first_name=first_name,
                    last_name=last_name,
                    type=USER_TYPE_PROJECT_OWNER,
                    source=USER_SOURCE_TASK_WIZARD)
                if new_user:
                    validated_data.update({'user': new_user})
                    user_signed_up.send(sender=get_user_model(),
                                        request=None,
                                        user=new_user)

            instance = super(TaskSerializer, self).create(validated_data)

        self.save_skills(instance, skills)
        self.save_participants(instance, participants)
        self.save_participation(instance, participation)
        self.save_milestones(instance, milestones)
        self.save_ratings(instance, ratings)

        if is_update:
            if not initial_approved and instance.approved:
                task_approved.send(sender=Task, task=instance)

            if initial_apply and not instance.apply:
                task_applications_closed.send(sender=Task, task=instance)

            if not initial_closed and instance.closed:
                task_closed.send(sender=Task, task=instance)
        else:
            # Triggered here instead of in the post_save signal to allow skills to be attached first
            # TODO: Consider moving this trigger
            notify_new_task.delay(instance.id, new_user=bool(new_user))
        return instance

    def create(self, validated_data):
        return self.save_task(validated_data)

    def update(self, instance, validated_data):
        return self.save_task(validated_data, instance=instance)

    def save_skills(self, task, skills):
        if skills is not None:
            task.skills = skills
            task.save()

    def save_participation(self, task, participation):
        if participation:
            new_assignee = None
            for item in participation:
                if 'accepted' in item and item.get('accepted', False):
                    item['activated_at'] = datetime.datetime.utcnow()
                defaults = item
                if isinstance(defaults, dict):
                    current_user = self.get_current_user()
                    participation_creator = task.user
                    if current_user and current_user.is_authenticated(
                    ) and current_user != item.get('user', None):
                        participation_creator = current_user
                    defaults['created_by'] = participation_creator

                try:
                    participation_obj, created = Participation.objects.update_or_create(
                        task=task, user=item['user'], defaults=defaults)
                    if (not created) and 'accepted' in item:
                        participation_response.send(
                            sender=Participation,
                            participation=participation_obj)
                    if 'assignee' in item and item['assignee']:
                        new_assignee = item['user']
                except:
                    pass
            if new_assignee:
                Participation.objects.exclude(user=new_assignee).filter(
                    task=task).update(assignee=False)

    def save_milestones(self, task, milestones):
        if milestones:
            for item in milestones:
                event_type = item.get('type', PROGRESS_EVENT_TYPE_MILESTONE)
                if event_type != PROGRESS_EVENT_TYPE_MILESTONE:
                    continue
                defaults = {'created_by': self.get_current_user() or task.user}
                defaults.update(item)
                try:
                    ProgressEvent.objects.update_or_create(
                        task=task,
                        type=event_type,
                        due_at=item['due_at'],
                        defaults=defaults)
                except:
                    pass

    def save_ratings(self, task, ratings):
        if ratings:
            for item in ratings:
                try:
                    Rating.objects.update_or_create(
                        content_type=ContentType.objects.get_for_model(task),
                        object_id=task.id,
                        criteria=item['criteria'],
                        defaults=item)
                except:
                    pass

    def save_participants(self, task, participants):
        # TODO: Remove and move existing code to using save_participation
        if participants:
            assignee = self.initial_data.get('assignee', None)
            confirmed_participants = self.initial_data.get(
                'confirmed_participants', None)
            rejected_participants = self.initial_data.get(
                'rejected_participants', None)
            created_by = self.get_current_user() or task.user

            changed_assignee = False
            for user in participants:
                try:
                    defaults = {'created_by': created_by}
                    if assignee:
                        defaults['assignee'] = bool(user.id == assignee)
                    if rejected_participants and user.id in rejected_participants:
                        defaults['accepted'] = False
                        defaults['responded'] = True
                    if confirmed_participants and user.id in confirmed_participants:
                        defaults['accepted'] = True
                        defaults['responded'] = True
                        defaults['activated_at'] = datetime.datetime.utcnow()

                    participation_obj, created = Participation.objects.update_or_create(
                        task=task, user=user, defaults=defaults)
                    if (not created) and (user.id in rejected_participants or
                                          user.id in confirmed_participants):
                        participation_response.send(
                            sender=Participation,
                            participation=participation_obj)
                    if user.id == assignee:
                        changed_assignee = True
                except:
                    pass
            if assignee and changed_assignee:
                Participation.objects.exclude(user__id=assignee).filter(
                    task=task).update(assignee=False)

    def get_display_fee(self, obj):
        user = self.get_current_user()
        amount = None
        if not obj.pay:
            return None
        if user and user.is_developer:
            amount = obj.pay_dev * (1 - obj.tunga_ratio_dev)
        return obj.display_fee(amount=amount)

    def get_can_apply(self, obj):
        if obj.closed or not obj.apply or not obj.is_developer_ready:
            return False
        user = self.get_current_user()
        if user:
            if obj.user == user or not user.is_developer or user.pending or not profile_check(
                    user):
                return False
            return obj.applicants.filter(id=user.id).count() == 0 and \
                   obj.participation_set.filter(user=user).count() == 0
        return False

    def get_can_claim(self, obj):
        if obj.closed or obj.is_task:
            return False
        user = self.get_current_user()
        if user and user.is_authenticated() and (
                user.is_project_manager or user.is_admin) and not obj.pm and (
                    obj.pm_required or obj.source == TASK_SOURCE_NEW_USER):
            return True
        return False

    def get_can_return(self, obj):
        if obj.closed or obj.estimate:
            return False
        user = self.get_current_user()
        if user and user.is_authenticated() and obj.pm == user:
            return True
        return False

    def get_can_save(self, obj):
        return False

    def get_is_participant(self, obj):
        user = self.get_current_user()
        if user:
            return obj.subtask_participants_inclusive_filter.filter(
                (Q(accepted=True) | Q(responded=False)), user=user).count() > 0
        return False

    def get_is_admin(self, obj):
        user = self.get_current_user()
        return obj.has_admin_access(user)

    def get_my_participation(self, obj):
        user = self.get_current_user()
        if user:
            try:
                participation = obj.participation_set.get(user=user)
                return {
                    'id': participation.id,
                    'user': participation.user.id,
                    'assignee': participation.assignee,
                    'accepted': participation.accepted,
                    'responded': participation.responded
                }
            except:
                pass
        return None