class TimeEntrySerializer(ContentTypeAnnotatedModelSerializer, DetailAnnotatedModelSerializer): user = SimpleUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) class Meta: model = TimeEntry exclude = () read_only_fields = ('created_at', 'updated_at') details_serializer = TimeEntryDetailsSerializer
class NestedProgressEventSerializer(ContentTypeAnnotatedModelSerializer): created_by = SimpleUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) report = BasicProgressReportSerializer(read_only=True, required=False, source='progressreport') class Meta: model = ProgressEvent exclude = ('task', 'created_at')
def get(self, request): user = self.get_object(request) if user is None: return Response( { 'status': 'Unauthorized', 'message': 'You are not logged in' }, status=status.HTTP_401_UNAUTHORIZED) serializer = SimpleUserSerializer(user) return Response(serializer.data)
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(), Comment: CommentSerializer(), Connection: ConnectionSerializer(), Task: SimpleTaskSerializer(), Application: ApplicationSerializer(), Participation: ParticipationSerializer(), TaskRequest: TaskRequestSerializer(), ProgressEvent: SimpleProgressEventSerializer(), ProgressReport: SimpleProgressReportSerializer(), Integration: SimpleIntegrationSerializer(), IntegrationActivity: SimpleIntegrationActivitySerializer() }) class Meta(SimpleActivitySerializer.Meta): pass def get_actor_type(self, obj): return get_instance_type(obj.actor) def get_target_type(self, obj): return get_instance_type(obj.target)
class MessageSerializer(serializers.ModelSerializer, GetCurrentUserAnnotatedSerializerMixin): user = SimpleUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) excerpt = serializers.CharField(required=False, read_only=True) attachments = SimpleAttachmentSerializer(read_only=True, required=False, many=True) class Meta: model = Message read_only_fields = ('created_at', )
class ProgressEventSerializer(ContentTypeAnnotatedModelSerializer, DetailAnnotatedModelSerializer): created_by = SimpleUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) report = SimpleProgressReportSerializer(read_only=True, required=False, source='progressreport') class Meta: model = ProgressEvent exclude = () read_only_fields = ('created_at', ) details_serializer = ProgressEventDetailsSerializer
class TaskDetailsSerializer(ContentTypeAnnotatedModelSerializer): project = SimpleProjectSerializer() user = SimpleUserSerializer() skills = SkillSerializer(many=True) assignee = SimpleParticipationSerializer(required=False, read_only=True) applications = SimpleApplicationSerializer(many=True, source='application_set') participation = SimpleParticipationSerializer(many=True, source='participation_set') class Meta: model = Task fields = ('project', 'user', 'skills', 'assignee', 'applications', 'participation')
class MessageSerializer(serializers.ModelSerializer, GetCurrentUserAnnotatedSerializerMixin): user = SimpleUserSerializer(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) html_body = serializers.CharField(required=False, read_only=True) class Meta: model = Message exclude = ('alt_user', 'source', 'extra') read_only_fields = ('created_at', )
class ProgressReportSerializer(ContentTypeAnnotatedModelSerializer, DetailAnnotatedModelSerializer): user = SimpleUserSerializer(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 exclude = () read_only_fields = ('created_at', ) details_serializer = ProgressReportDetailsSerializer
class BlogPostSerializer(serializers.ModelSerializer): created_by = SimpleUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) class Meta: model = BlogPost fields = '__all__' extra_kwargs = { 'slug': { 'required': False, 'allow_blank': True, 'allow_null': True }, }
class ProjectSerializer(ContentTypeAnnotatedModelSerializer, DetailAnnotatedModelSerializer): user = SimpleUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) excerpt = serializers.CharField(required=False, read_only=True) deadline = serializers.DateTimeField(required=False, allow_null=True) tasks = serializers.PrimaryKeyRelatedField(required=False, read_only=True, many=True) class Meta: model = Project exclude = () read_only_fields = ('created_at', ) details_serializer = ProjectDetailsSerializer
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(), LegacyUpload: LegacyUploadSerializer(), Connection: ConnectionSerializer(), Task: SimpleTaskSerializer(), Application: ApplicationSerializer(), LegacyParticipation: LegacyParticipationSerializer(), Estimate: SimpleEstimateSerializer(), Quote: SimpleQuoteSerializer(), Sprint: SimpleSprintSerializer(), LegacyProgressEvent: LegacySimpleProgressEventSerializer(), LegacyProgressReport: LegacyProgressReportSerializer(), Integration: SimpleIntegrationSerializer(), IntegrationActivity: SimpleIntegrationActivitySerializer(), Project: ProjectSerializer(), Document: DocumentSerializer(), Participation: ParticipationSerializer(), ProgressEvent: ProgressEventSerializer(), ProgressReport: ProgressReportSerializer(), Invoice: InvoiceSerializer(), Payment: PaymentSerializer(), Upload: UploadSerializer(), FieldChangeLog: FieldChangeLogSerializer(), }, 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)
class ParticipationSerializer(ContentTypeAnnotatedModelSerializer, DetailAnnotatedModelSerializer): created_by = SimpleUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) class Meta: model = Participation exclude = () read_only_fields = ('created_at', ) details_serializer = ParticipationDetailsSerializer def update(self, instance, validated_data): initial_responded = instance.responded if validated_data.get('accepted'): validated_data['responded'] = True validated_data['activated_at'] = datetime.datetime.utcnow() instance = super(ParticipationSerializer, self).update(instance, validated_data) if not initial_responded and instance.accepted or instance.responded: participation_response.send(sender=Participation, participation=instance) return instance
class ApplicationSerializer(ContentTypeAnnotatedModelSerializer, DetailAnnotatedModelSerializer): user = SimpleUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) class Meta: model = Application exclude = () details_serializer = ApplicationDetailsSerializer extra_kwargs = { 'pitch': { 'required': True, 'allow_blank': False, 'allow_null': False }, 'hours_needed': { 'required': True, 'allow_null': False }, #'hours_available': {'required': True, 'allow_null': False}, 'deliver_at': { 'required': True, 'allow_null': False } } def update(self, instance, validated_data): initial_responded = instance.responded if validated_data.get('accepted'): validated_data['responded'] = True instance = super(ApplicationSerializer, self).update(instance, validated_data) if not initial_responded and instance.accepted or instance.responded: application_response.send(sender=Application, application=instance) return instance
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
class ChannelDetailsSerializer(serializers.ModelSerializer): participants = SimpleUserSerializer(many=True) class Meta: model = Channel fields = ('participants', )
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
class SimpleChannelSerializer(serializers.ModelSerializer): created_by = SimpleUserSerializer() class Meta: model = Channel exclude = ('participants', )
class ProfileDetailsSerializer(SimpleProfileSerializer): user = SimpleUserSerializer() class Meta: model = UserProfile fields = ('user', 'city', 'skills')
class TungaTokenSerializer(TokenSerializer): user = SimpleUserSerializer(read_only=True, required=False) class Meta: model = TokenSerializer.Meta.model fields = ('key', 'user')
class SimpleApplicationSerializer(ContentTypeAnnotatedModelSerializer): user = SimpleUserSerializer() class Meta: model = Application exclude = ('created_at', )
class AbstractEstimateSerializer(ContentTypeAnnotatedModelSerializer, DetailAnnotatedModelSerializer, GetCurrentUserAnnotatedSerializerMixin): user = SimpleUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) moderated_by = SimpleUserSerializer(required=False, read_only=True) activities = NestedWorkActivitySerializer(required=True, read_only=False, many=True) class Meta: model = AbstractEstimate fields = '__all__' read_only_fields = ('submitted_at', 'moderated_at', 'reviewed_at', 'created_at', 'updated_at') details_serializer = AbstractEstimateDetailsSerializer def validate_activities(self, value): if not value: raise ValidationError('This field is required') return value def pop_related_objects(self, validated_data, instance=None): self.activities = None if 'activities' in validated_data: self.activities = validated_data.pop('activities') def save_related_objects(self, instance): self.save_activities(instance, self.activities) def on_create_complete(self, instance): pass def on_status_change(self, instance): pass def save_estimate(self, validated_data, instance=None): self.pop_related_objects(validated_data, instance) initial_status = STATUS_INITIAL is_update = bool(instance) if is_update: initial_status = instance.status # Clone new estimate if previous was declined or rejected and reset useful defaults if instance.status in [STATUS_DECLINED, STATUS_REJECTED]: instance.pk = None instance.status = STATUS_INITIAL instance.moderated_by = None instance.moderated_at = None instance.reviewed_by = None instance.moderated_at = None instance.save() if initial_status != validated_data.get('status'): if validated_data.get('status') == STATUS_SUBMITTED: validated_data['submitted_at'] = datetime.datetime.utcnow() if validated_data.get('status') in [ STATUS_APPROVED, STATUS_DECLINED ]: validated_data['moderated_by'] = self.get_current_user( ) or None validated_data['moderated_at'] = datetime.datetime.utcnow() if validated_data.get('status') in [ STATUS_ACCEPTED, STATUS_REJECTED ]: validated_data['reviewed_by'] = self.get_current_user( ) or None validated_data['reviewed_at'] = datetime.datetime.utcnow() instance = super(AbstractEstimateSerializer, self).update(instance, validated_data) else: instance = super(AbstractEstimateSerializer, self).create(validated_data) self.save_related_objects(instance) if is_update: if initial_status != instance.status: self.on_status_change(instance) else: # Triggered here instead of in the post_save signal to allow related objects to be attached first self.on_create_complete(instance) return instance def create(self, validated_data): return self.save_estimate(validated_data) def update(self, instance, validated_data): return self.save_estimate(validated_data, instance=instance) def save_activities(self, instance, activities): if activities: c_type = ContentType.objects.get_for_model(self.Meta.model) # Delete existing WorkActivity.objects.filter(content_type=c_type, object_id=instance.id).delete() for item in activities: try: item['content_type'] = c_type item['object_id'] = instance.id item['user'] = self.get_current_user() WorkActivity.objects.create(**item) except: pass
class IntegrationSerializer(ContentTypeAnnotatedModelSerializer, GetCurrentUserAnnotatedSerializerMixin): created_by = SimpleUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) events = serializers.PrimaryKeyRelatedField( many=True, queryset=IntegrationEvent.objects.all(), required=False, read_only=False) meta = NestedIntegrationMetaSerializer(required=False, read_only=False, many=True, source='integrationmeta_set') # Write Only repo = serializers.JSONField(required=False, write_only=True, allow_null=True) issue = serializers.JSONField(required=False, write_only=True, allow_null=True) project = serializers.JSONField(required=False, write_only=True, allow_null=True) team = serializers.JSONField(required=False, write_only=True, allow_null=True) channel = serializers.JSONField(required=False, write_only=True, allow_null=True) # Read Only repo_id = serializers.CharField(required=False, read_only=True) issue_id = serializers.CharField(required=False, read_only=True) project_id = serializers.CharField(required=False, read_only=True) project_task_id = serializers.CharField(required=False, read_only=True) team_id = serializers.CharField(required=False, read_only=True) team_name = serializers.CharField(required=False, read_only=True) channel_id = serializers.CharField(required=False, read_only=True) channel_name = serializers.CharField(required=False, read_only=True) class Meta: model = Integration exclude = ('secret', ) read_only_fields = ('created_at', 'updated_at') def send_creation_signal(self, instance): task_integration.send(sender=Integration, integration=instance) def save_integration(self, validated_data, instance=None): events = None if 'events' in validated_data: events = validated_data.pop('events') meta = None if 'meta' in validated_data: meta = validated_data.pop('meta') metadata_objects = dict() metadata_keys = ['repo', 'issue', 'project', 'team', 'channel'] for key in metadata_keys: if key in validated_data: metadata_objects[key] = validated_data.pop(key) if instance: instance = super(IntegrationSerializer, self).update(instance, validated_data) else: instance = super(IntegrationSerializer, self).create(validated_data) self.save_events(instance, events) self.save_meta(instance, meta) for key in metadata_keys: self.save_meta_object(instance, metadata_objects.get(key, None), key) self.send_creation_signal(instance) return instance def create(self, validated_data): return self.save_integration(validated_data) def update(self, instance, validated_data): return self.save_integration(validated_data, instance=instance) def save_events(self, instance, events): if events: instance.events.clear() for item in events: try: instance.events.add(item) except: pass def save_meta(self, instance, meta): if meta: for item in meta: defaults = { 'created_by': self.get_current_user() or instance.user } defaults.update(item) try: IntegrationMeta.objects.update_or_create( integration=instance, meta_key=item['meta_key'], defaults=defaults) except: pass def save_meta_object(self, instance, meta_object, prefix): if meta_object: for key in meta_object: defaults = { 'created_by': self.get_current_user() or instance.user, 'meta_key': '%s_%s' % (prefix, key), 'meta_value': clean_meta_value(meta_object[key]) } try: IntegrationMeta.objects.update_or_create( integration=instance, meta_key=defaults['meta_key'], defaults=defaults) except: pass
class SimpleProjectSerializer(ContentTypeAnnotatedModelSerializer): user = SimpleUserSerializer() class Meta: model = Project
def get_user(self, obj): return SimpleUserSerializer(obj.user).data
class SimpleParticipationSerializer(BasicParticipationSerializer): user = SimpleUserSerializer() class Meta: model = Participation exclude = ('created_at', )
class BasicProgressEventSerializer(ContentTypeAnnotatedModelSerializer): created_by = SimpleUserSerializer() class Meta: model = ProgressEvent fields = '__all__'