class InterestPollSerializer(NestedModelSerializer, ContentTypeAnnotatedModelSerializer): created_by = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) project = SimpleProjectSerializer() user = SimplestUserSerializer() class Meta: model = InterestPoll fields = '__all__' read_only_fields = ('created_at', 'updated_at') def nested_save_override(self, validated_data, instance=None): initial_status = None initial_approval_status = None if instance: initial_status = instance.status initial_approval_status = instance.approval_status instance = super(InterestPollSerializer, self).nested_save_override(validated_data, instance=instance) if instance: if initial_status != instance.status: post_field_update.send(sender=InterestPoll, instance=instance, field='status') if initial_approval_status != instance.approval_status: post_field_update.send(sender=InterestPoll, instance=instance, field='approval_status') return instance
class SimpleInterestPollSerializer(SimpleModelSerializer): created_by = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) user = SimplestUserSerializer() class Meta: model = InterestPoll exclude = ('project',)
class ConnectionSerializer(NestedModelSerializer, ContentTypeAnnotatedModelSerializer): from_user = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) to_user = SimplestUserSerializer(required=False, read_only=False) class Meta: model = Connection fields = '__all__'
class InvoiceSerializer(NestedModelSerializer, ContentTypeAnnotatedModelSerializer): created_by = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) updated_by = SimplestUserSerializer(required=False, read_only=True, default=CurrentUserDefault()) project = NestedProjectSerializer() user = SimplestUserSerializer() full_title = serializers.CharField(read_only=True) milestone = SimpleProgressEventSerializer(required=False, allow_null=True) tax_amount = serializers.DecimalField(max_digits=17, decimal_places=2, read_only=True) subtotal = serializers.DecimalField(max_digits=17, decimal_places=2, read_only=True) total_amount = serializers.DecimalField(max_digits=17, decimal_places=2, read_only=True) download_url = serializers.CharField(read_only=True) due_at = serializers.DateTimeField(read_only=True) is_overdue = serializers.BooleanField(read_only=True) class Meta: model = Invoice fields = '__all__'
class NestedProjectSerializer(SimpleModelSerializer): user = SimplestUserSerializer(required=False, read_only=True) owner = SimplestUserSerializer(required=False, read_only=True) pm = SimplestUserSerializer(required=False, read_only=True) class Meta: model = Project fields = '__all__'
class ParticipationSerializer(NestedModelSerializer, ContentTypeAnnotatedModelSerializer): created_by = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) project = SimpleProjectSerializer() user = SimplestUserSerializer() class Meta: model = Participation fields = '__all__' read_only_fields = ('created_at', 'updated_at')
class ProjectSerializer( NestedModelSerializer, ContentTypeAnnotatedModelSerializer, GetCurrentUserAnnotatedSerializerMixin ): user = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) owner = SimplestUserSerializer(required=False, allow_null=True) pm = SimplestUserSerializer(required=False, allow_null=True) skills = SimpleSkillSerializer(required=False, many=True) participation = SimpleParticipationSerializer(required=False, many=True, source='participation_set') documents = SimpleDocumentSerializer(required=False, many=True, source='document_set') progress_events = SimpleProgressEventSerializer(required=False, many=True, source='progressevent_set') meta = SimpleProjectMetaSerializer(required=False, many=True, source='projectmeta_set') change_log = serializers.JSONField(required=False, write_only=True) margin = serializers.ReadOnlyField() interest_polls = SimpleInterestPollSerializer(required=False, many=True, source='interestpoll_set') expected_duration = serializers.CharField(required=True, read_only=False, allow_null=False, allow_blank=False) category = serializers.CharField(required=True, read_only=False, allow_null=False, allow_blank=False) class Meta: model = Project fields = '__all__' read_only_fields = ('created_at', 'updated_at') def nested_save_override(self, validated_data, instance=None): initial_stage = None initial_archived = None if instance: initial_stage = instance.stage initial_archived = instance.archived instance = super(ProjectSerializer, self).nested_save_override(validated_data, instance=instance) if instance: if initial_stage != instance.stage: post_field_update.send(sender=Project, instance=instance, field='stage') if type(initial_archived) == bool and instance.archived and not initial_archived: complete_exact_sync.delay(instance.id) return instance def save_nested_skills(self, data, instance, created=False): if data is not None: instance.skills = ', '.join([skill.get('name', '') for skill in data]) instance.save() def save_nested_change_log(self, data, instance, created=False): if data is not None: for item in data: FieldChangeLog.objects.create(content_object=instance, created_by=self.get_current_user(), **item)
class SimpleDocumentSerializer(SimpleModelSerializer): created_by = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) download_url = serializers.CharField(read_only=True) class Meta: model = Document exclude = ('project',)
class NestedProgressEventSerializer(SimpleModelSerializer): created_by = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) project = SimpleProjectSerializer(required=False, read_only=True) class Meta: model = ProgressEvent fields = '__all__'
class CompanySerializer(NestedModelSerializer, ContentTypeAnnotatedModelSerializer): user = SimplestUserSerializer(required=False, read_only=False) city = serializers.CharField(required=False, allow_blank=True, allow_null=True) skills = SimpleSkillSerializer(required=False, many=True) country = CountryField(required=False) country_name = serializers.CharField(required=False, read_only=True) class Meta: model = Company fields = '__all__' def save_nested_user(self, data, instance, created=False): user = instance.user if user: user.first_name = data.get('first_name', user.first_name) user.last_name = data.get('last_name', user.last_name) image = data.get('image', None) if image: user.image = image user.save() def save_nested_skills(self, data, instance, created=False): if data is not None: instance.skills = ', '.join( [skill.get('name', '') for skill in data]) instance.save() def save_nested_city(self, data, instance, created=False): if data: instance.city = data instance.save()
class UploadSerializer(serializers.ModelSerializer): user = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) url = serializers.CharField(required=False, read_only=True, source='file.url') name = serializers.SerializerMethodField(required=False, read_only=True) size = serializers.IntegerField(required=False, read_only=True, source='file.size') display_size = serializers.SerializerMethodField(required=False, read_only=True) class Meta: model = Upload fields = '__all__' def get_name(self, obj): return obj.file.name.split('/')[-1] def get_display_size(self, obj): filesize = obj.file.size converter = {'KB': 10**3, 'MB': 10**6, 'GB': 10**9, 'TB': 10**12} units = ['TB', 'GB', 'MB', 'KB'] for label in units: conversion = converter[label] if conversion and filesize > conversion: return '%s %s' % (round(filesize / conversion, 2), label) return '%s %s' % (filesize, 'bytes')
class SimpleProgressEventSerializer(SimpleModelSerializer): created_by = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) class Meta: model = ProgressEvent exclude = ('project', )
class NotificationReadLogSerializer(serializers.ModelSerializer): user = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) class Meta: model = NotificationReadLog fields = '__all__'
class SimpleProgressReportSerializer(SimpleModelSerializer): user = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) status_display = serializers.CharField(required=False, read_only=True, source='get_status_display') stuck_reason_display = serializers.CharField(required=False, read_only=True, source='get_stuck_reason_display') class Meta: model = ProgressReport exclude = ('event',)
class ProfileSerializer(NestedModelSerializer, ContentTypeAnnotatedModelSerializer): user = SimplestUserSerializer(required=False) city = serializers.CharField(required=False, allow_blank=True, allow_null=True) skills = SimpleSkillSerializer(required=False, many=True) skills_details = SkillsDetailsSerializer(required=False, read_only=True) country = CountryField(required=False) country_name = serializers.CharField(required=False, read_only=True) class Meta: model = UserProfile fields = '__all__' def nested_save_override(self, validated_data, instance=None): initial_bio = None initial_location = None initial_skills = None if instance: initial_bio = instance.bio initial_location = instance.location initial_skills = [skill.name for skill in instance.skills.all()] list.sort(initial_skills) instance = super(ProfileSerializer, self).nested_save_override(validated_data, instance=instance) final_skills = [skill.name for skill in instance.skills.all()] list.sort(final_skills) if not instance or initial_bio != instance.bio or initial_location != instance.location or initial_skills != final_skills: user_profile_updated.send(sender=UserProfile, profile=instance) return instance def save_nested_user(self, data, instance, created=False): user = instance.user if user: user.first_name = data.get('first_name', user.first_name) user.last_name = data.get('last_name', user.last_name) image = data.get('image', None) if image: user.image = image user.save() def save_nested_skills(self, data, instance, created=False): if data is not None: instance.skills = ', '.join([skill.get('name', '') for skill in data]) instance.save() for skill in data: try: category = skill.get('type', None) if category: Skill.objects.filter(name=skill.get('name', ''), type=SKILL_TYPE_OTHER).update(type=category) except: pass def save_nested_city(self, data, instance, created=False): if data: instance.city = data instance.save()
class DocumentSerializer(NestedModelSerializer, ContentTypeAnnotatedModelSerializer): created_by = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) project = SimpleProjectSerializer() download_url = serializers.CharField(required=False, read_only=True) class Meta: model = Document fields = '__all__' read_only_fields = ('created_at', 'updated_at')
class PaymentSerializer(NestedModelSerializer, ContentTypeAnnotatedModelSerializer): created_by = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) invoice = SimpleInvoiceSerializer() class Meta: model = Payment fields = '__all__'
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',)
class SimpleInvoiceSerializer(SimpleModelSerializer): created_by = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) updated_by = SimplestUserSerializer(required=False, read_only=True, default=CurrentUserDefault()) user = SimplestUserSerializer() full_title = serializers.CharField(read_only=True) tax_amount = serializers.DecimalField(max_digits=17, decimal_places=2, read_only=True) total_amount = serializers.DecimalField(max_digits=17, decimal_places=2, read_only=True) download_url = serializers.CharField(read_only=True) due_at = serializers.DateTimeField(read_only=True) is_overdue = serializers.BooleanField(read_only=True) class Meta: model = Invoice exclude = ('project', )
class FieldChangeLogSerializer(serializers.ModelSerializer): target_type = serializers.SerializerMethodField() target = GenericRelatedField( { Project: ProjectSerializer(), ProgressEvent: ProgressEventSerializer(), }, source='content_object') created_by = SimplestUserSerializer() class Meta: model = FieldChangeLog fields = '__all__' def get_target_type(self, obj): return get_instance_type(obj.content_object)
class ProgressEventSerializer(NestedModelSerializer, ContentTypeAnnotatedModelSerializer, GetCurrentUserAnnotatedSerializerMixin): created_by = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) project = SimpleProjectSerializer() progress_reports = SimpleProgressReportSerializer( required=False, read_only=True, many=True, source='progressreport_set' ) change_log = serializers.JSONField(required=False, write_only=True) class Meta: model = ProgressEvent fields = '__all__' read_only_fields = ('created_at', 'updated_at') def save_nested_change_log(self, data, instance, created=False): if data is not None: for item in data: FieldChangeLog.objects.create(content_object=instance, created_by=self.get_current_user(), **item)
class ChannelSerializer(NestedModelSerializer, GetCurrentUserAnnotatedSerializerMixin): created_by = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) display_type = serializers.CharField(required=False, read_only=True, source='get_type_display') participants = SimplestUserSerializer(required=True, many=True) 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') 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 SimpleChannelSerializer(serializers.ModelSerializer): created_by = SimplestUserSerializer() class Meta: model = Channel exclude = ('participants',)
class ProgressReportSerializer(NestedModelSerializer, ContentTypeAnnotatedModelSerializer, GetCurrentUserAnnotatedSerializerMixin): user = SimplestUserSerializer(required=False, read_only=True, default=CreateOnlyCurrentUserDefault()) event = NestedProgressEventSerializer() status_display = serializers.CharField(required=False, read_only=True, source='get_status_display') stuck_reason_display = serializers.CharField(required=False, read_only=True, source='get_stuck_reason_display') class Meta: model = ProgressReport fields = '__all__' read_only_fields = ('created_at', 'updated_at') def validate(self, attrs): errors = dict() current_user = self.get_current_user() if current_user.is_authenticated(): BOOLEANS = (True, False) required_fields = [] status_schema = ( 'status', [status_item[0] for status_item in PROGRESS_REPORT_STATUS_CHOICES], [ ( [PROGRESS_REPORT_STATUS_BEHIND_AND_STUCK, PROGRESS_REPORT_STATUS_STUCK], 'stuck_reason', [stuck_reason_item[0] for stuck_reason_item in PROGRESS_REPORT_STUCK_REASON_CHOICES] ) ] ) rate_deliverables_schema = ('rate_deliverables', list(range(1, 6))) # 1...5 if current_user.is_developer: required_fields = [ status_schema, 'started_at', ('percentage', list(range(0, 101))), # 0...100 'accomplished', rate_deliverables_schema, 'todo', 'next_deadline', ( 'next_deadline_meet', BOOLEANS, [ (False, 'next_deadline_fail_reason') ] ) ] elif current_user.is_project_manager: required_fields = [ status_schema, ( 'last_deadline_met', BOOLEANS, [ (False, 'deadline_miss_communicated', BOOLEANS), (False, 'deadline_report') ] ), 'percentage', 'accomplished', 'todo', 'next_deadline', ( 'next_deadline_meet', BOOLEANS, [ (False, 'next_deadline_fail_reason') ] ), 'team_appraisal' ] elif current_user.is_project_owner: required_fields = [ ( 'last_deadline_met', BOOLEANS, [ (False, 'deadline_miss_communicated', BOOLEANS) ] ), ('deliverable_satisfaction', BOOLEANS), rate_deliverables_schema, ('pm_communication', BOOLEANS) ] errors.update(validate_field_schema(required_fields, attrs, raise_exception=False)) if errors: raise ValidationError(errors) return attrs