Пример #1
0
    def create(self, validated_data):
        """Over-ridden to handle nested writes."""
        update = super(IndicatorPeriodDataFrameworkSerializer, self).create(validated_data)
        for disaggregation in self._disaggregations_data:
            disaggregation['update'] = update.id
            serializer = DisaggregationSerializer(data=disaggregation)
            serializer.is_valid(raise_exception=True)
            serializer.create(serializer.validated_data)

        return update
Пример #2
0
    def update(self, instance, validated_data):
        """Over-ridden to handle nested updates."""
        super(IndicatorPeriodDataFrameworkSerializer, self).update(instance, validated_data)
        for disaggregation in self._disaggregations_data:
            disaggregation['update'] = instance.id
            serializer = DisaggregationSerializer(data=disaggregation)
            serializer.is_valid(raise_exception=True)
            disaggregation_instance, _ = instance.disaggregations.get_or_create(
                update=instance,
                dimension=serializer.validated_data['dimension'],
            )
            serializer.update(disaggregation_instance, serializer.validated_data)

        return instance._meta.model.objects.select_related(
            'period',
            'user',
            'approved_by',
        ).prefetch_related(
            'comments',
            'disaggregations',
        ).get(id=instance.id)
Пример #3
0
    def create(self, validated_data):
        self._validate_disaggregations(
            self._disaggregations_data,
            value=ensure_decimal(validated_data.get('value', 0)),
            numerator=ensure_decimal(validated_data.get('numerator', None)),
            denominator=ensure_decimal(validated_data.get('denominator',
                                                          None)))
        """Over-ridden to handle nested writes."""
        files = validated_data.pop('files', [])
        photos = validated_data.pop('photos', [])
        comments = validated_data.pop('comments', [])
        update = super(IndicatorPeriodDataFrameworkSerializer,
                       self).create(validated_data)
        for disaggregation in self._disaggregations_data:
            disaggregation['update'] = update.id
            if 'type_id' in disaggregation and 'dimension_value' not in disaggregation:
                disaggregation['dimension_value'] = disaggregation['type_id']
            serializer = DisaggregationSerializer(data=disaggregation)
            serializer.is_valid(raise_exception=True)
            serializer.create(serializer.validated_data)
        for file in files:
            IndicatorPeriodDataFile.objects.create(update=update, file=file)
        for photo in photos:
            IndicatorPeriodDataPhoto.objects.create(update=update, photo=photo)
        for comment in comments:
            IndicatorPeriodDataComment.objects.create(
                data=update, user=update.user, comment=comment['comment'])

        return update
Пример #4
0
    def update(self, instance, validated_data):
        self._validate_disaggregations(
            self._disaggregations_data,
            value=ensure_decimal(validated_data.get('value', instance.value)),
            numerator=ensure_decimal(
                validated_data.get('numerator', instance.numerator)),
            denominator=ensure_decimal(
                validated_data.get('denominator', instance.denominator)),
            update=instance)
        """Over-ridden to handle nested updates."""
        files = validated_data.pop('files', [])
        photos = validated_data.pop('photos', [])
        comments = validated_data.pop('comments', [])
        super(IndicatorPeriodDataFrameworkSerializer,
              self).update(instance, validated_data)
        for disaggregation in self._disaggregations_data:
            disaggregation['update'] = instance.id
            serializer = DisaggregationSerializer(data=disaggregation)
            serializer.is_valid(raise_exception=True)
            disaggregation_instance, _ = instance.disaggregations.get_or_create(
                update=instance,
                dimension_value=serializer.validated_data['dimension_value'],
            )
            serializer.update(disaggregation_instance,
                              serializer.validated_data)
        for file in files:
            IndicatorPeriodDataFile.objects.create(update=instance, file=file)
        for photo in photos:
            IndicatorPeriodDataPhoto.objects.create(update=instance,
                                                    photo=photo)
        for comment in comments:
            comment_id = int(comment.get('id', 0))
            comment_txt = str(comment.get('comment', ''))
            if not comment_id:
                IndicatorPeriodDataComment.objects.create(
                    data=instance,
                    user=instance.user,
                    comment=comment['comment'])
            else:
                comment_obj = IndicatorPeriodDataComment.objects.get(
                    id=comment_id)
                if not comment_txt:
                    comment_obj.delete()
                else:
                    comment_obj.comment = comment_txt
                    comment_obj.save()

        return instance._meta.model.objects.select_related(
            'period',
            'user',
            'approved_by',
        ).prefetch_related(
            'comments',
            'disaggregations',
        ).get(id=instance.id)
Пример #5
0
    def create(self, validated_data):
        """Over-ridden to handle nested writes."""
        update = super(IndicatorPeriodDataFrameworkSerializer,
                       self).create(validated_data)
        for disaggregation in self._disaggregations_data:
            disaggregation['update'] = update.id
            serializer = DisaggregationSerializer(data=disaggregation)
            serializer.is_valid(raise_exception=True)
            serializer.create(serializer.validated_data)

        return update
Пример #6
0
    def update(self, instance, validated_data):
        """Over-ridden to handle nested updates."""
        super(IndicatorPeriodDataFrameworkSerializer,
              self).update(instance, validated_data)
        for disaggregation in self._disaggregations_data:
            disaggregation['update'] = instance.id
            serializer = DisaggregationSerializer(data=disaggregation)
            serializer.is_valid(raise_exception=True)
            disaggregation_instance, _ = instance.disaggregations.get_or_create(
                update=instance,
                dimension=serializer.validated_data['dimension'],
            )
            serializer.update(disaggregation_instance,
                              serializer.validated_data)

        return instance._meta.model.objects.select_related(
            'period',
            'user',
            'approved_by',
        ).prefetch_related(
            'comments',
            'disaggregations',
        ).get(id=instance.id)
Пример #7
0
class IndicatorPeriodDataFrameworkSerializer(BaseRSRSerializer):

    period = serializers.PrimaryKeyRelatedField(
        queryset=IndicatorPeriod.objects.all())
    user = serializers.PrimaryKeyRelatedField(
        queryset=get_user_model().objects.all())
    comments = IndicatorPeriodDataCommentSerializer(read_only=True,
                                                    many=True,
                                                    required=False)
    disaggregations = DisaggregationSerializer(many=True, required=False)
    user_details = UserDetailsSerializer(required=False, source='user')
    approver_details = UserDetailsSerializer(required=False,
                                             source='approved_by')
    status_display = serializers.ReadOnlyField()
    photo_url = serializers.ReadOnlyField()
    file_url = serializers.ReadOnlyField()

    class Meta:
        model = IndicatorPeriodData

    def create(self, validated_data):
        """Over-ridden to handle nested writes."""
        update = super(IndicatorPeriodDataFrameworkSerializer,
                       self).create(validated_data)
        for disaggregation in self._disaggregations_data:
            disaggregation['update'] = update.id
            serializer = DisaggregationSerializer(data=disaggregation)
            serializer.is_valid(raise_exception=True)
            serializer.create(serializer.validated_data)

        return update

    def update(self, instance, validated_data):
        """Over-ridden to handle nested updates."""
        super(IndicatorPeriodDataFrameworkSerializer,
              self).update(instance, validated_data)
        for disaggregation in self._disaggregations_data:
            disaggregation['update'] = instance.id
            serializer = DisaggregationSerializer(data=disaggregation)
            serializer.is_valid(raise_exception=True)
            disaggregation_instance, _ = instance.disaggregations.get_or_create(
                update=instance,
                dimension=serializer.validated_data['dimension'],
            )
            serializer.update(disaggregation_instance,
                              serializer.validated_data)

        return instance._meta.model.objects.select_related(
            'period',
            'user',
            'approved_by',
        ).prefetch_related(
            'comments',
            'disaggregations',
        ).get(id=instance.id)

    def is_valid(self, raise_exception=False):
        # HACK to allow nested posting...
        self._disaggregations_data = self.initial_data.pop(
            'disaggregations', [])
        super(IndicatorPeriodDataFrameworkSerializer,
              self).is_valid(raise_exception=raise_exception)
Пример #8
0
class IndicatorPeriodDataFrameworkSerializer(BaseRSRSerializer):

    period = serializers.PrimaryKeyRelatedField(
        queryset=IndicatorPeriod.objects.all())
    comments = IndicatorPeriodDataCommentNestedSerializer(many=True,
                                                          required=False)
    disaggregations = DisaggregationSerializer(many=True, required=False)
    user_details = UserDetailsSerializer(read_only=True, source='user')
    approver_details = UserDetailsSerializer(read_only=True,
                                             source='approved_by')
    status_display = serializers.ReadOnlyField()
    photo_url = serializers.ReadOnlyField()
    file_url = serializers.ReadOnlyField()
    period_can_add_update = serializers.ReadOnlyField(
        source='period.can_save_update')
    files = serializers.ListField(child=serializers.FileField(),
                                  required=False,
                                  write_only=True)
    photos = serializers.ListField(child=serializers.FileField(),
                                   required=False,
                                   write_only=True)
    file_set = IndicatorPeriodDataFileSerializer(
        many=True, read_only=True, source='indicatorperioddatafile_set')
    photo_set = IndicatorPeriodDataPhotoSerializer(
        many=True, read_only=True, source='indicatorperioddataphoto_set')
    audit_trail = serializers.SerializerMethodField()

    class Meta:
        model = IndicatorPeriodData
        fields = '__all__'
        read_only_fields = ['user']

    def get_audit_trail(self, obj):
        entries = LogEntry.objects.filter(
            content_type=ContentType.objects.get_for_model(
                IndicatorPeriodData),
            object_id=obj.id,
            change_message__contains='"audit_trail": true')
        return [{
            'user': {
                'id': entry.user.id,
                'email': entry.user.email,
                'first_name': entry.user.first_name,
                'last_name': entry.user.last_name
            },
            'action_time':
            entry.action_time,
            'action_flag':
            'CHANGE' if entry.action_flag == CHANGE else
            'DELETION' if entry.action_flag == DELETION else 'ADDITION',
            'data':
            json.loads(entry.change_message)['data'],
        } for entry in entries]

    def create(self, validated_data):
        self._validate_disaggregations(
            self._disaggregations_data,
            value=ensure_decimal(validated_data.get('value', 0)),
            numerator=ensure_decimal(validated_data.get('numerator', None)),
            denominator=ensure_decimal(validated_data.get('denominator',
                                                          None)))
        """Over-ridden to handle nested writes."""
        files = validated_data.pop('files', [])
        photos = validated_data.pop('photos', [])
        comments = validated_data.pop('comments', [])
        update = super(IndicatorPeriodDataFrameworkSerializer,
                       self).create(validated_data)
        for disaggregation in self._disaggregations_data:
            disaggregation['update'] = update.id
            if 'type_id' in disaggregation and 'dimension_value' not in disaggregation:
                disaggregation['dimension_value'] = disaggregation['type_id']
            serializer = DisaggregationSerializer(data=disaggregation)
            serializer.is_valid(raise_exception=True)
            serializer.create(serializer.validated_data)
        for file in files:
            IndicatorPeriodDataFile.objects.create(update=update, file=file)
        for photo in photos:
            IndicatorPeriodDataPhoto.objects.create(update=update, photo=photo)
        for comment in comments:
            IndicatorPeriodDataComment.objects.create(
                data=update, user=update.user, comment=comment['comment'])

        return update

    def update(self, instance, validated_data):
        self._validate_disaggregations(
            self._disaggregations_data,
            value=ensure_decimal(validated_data.get('value', instance.value)),
            numerator=ensure_decimal(
                validated_data.get('numerator', instance.numerator)),
            denominator=ensure_decimal(
                validated_data.get('denominator', instance.denominator)),
            update=instance)
        """Over-ridden to handle nested updates."""
        files = validated_data.pop('files', [])
        photos = validated_data.pop('photos', [])
        comments = validated_data.pop('comments', [])
        super(IndicatorPeriodDataFrameworkSerializer,
              self).update(instance, validated_data)
        for disaggregation in self._disaggregations_data:
            disaggregation['update'] = instance.id
            serializer = DisaggregationSerializer(data=disaggregation)
            serializer.is_valid(raise_exception=True)
            disaggregation_instance, _ = instance.disaggregations.get_or_create(
                update=instance,
                dimension_value=serializer.validated_data['dimension_value'],
            )
            serializer.update(disaggregation_instance,
                              serializer.validated_data)
        for file in files:
            IndicatorPeriodDataFile.objects.create(update=instance, file=file)
        for photo in photos:
            IndicatorPeriodDataPhoto.objects.create(update=instance,
                                                    photo=photo)
        for comment in comments:
            comment_id = int(comment.get('id', 0))
            comment_txt = str(comment.get('comment', ''))
            if not comment_id:
                IndicatorPeriodDataComment.objects.create(
                    data=instance,
                    user=instance.user,
                    comment=comment['comment'])
            else:
                comment_obj = IndicatorPeriodDataComment.objects.get(
                    id=comment_id)
                if not comment_txt:
                    comment_obj.delete()
                else:
                    comment_obj.comment = comment_txt
                    comment_obj.save()

        return instance._meta.model.objects.select_related(
            'period',
            'user',
            'approved_by',
        ).prefetch_related(
            'comments',
            'disaggregations',
        ).get(id=instance.id)

    def _validate_disaggregations(self,
                                  disaggregations,
                                  value,
                                  numerator=None,
                                  denominator=None,
                                  update=None):
        adjustments = {}
        for disaggregation in disaggregations:
            type_id = disaggregation.get(
                'type_id', disaggregation.get('dimension_value', None))
            if type_id is None:
                continue
            if denominator is not None:
                disaggregation_denominator = ensure_decimal(
                    disaggregation.get('denominator', 0))
                if disaggregation_denominator > denominator:
                    raise serializers.ValidationError(
                        "disaggregations denominator should not exceed update denominator"
                    )
            category = IndicatorDimensionValue.objects.get(pk=type_id).name
            if category.id not in adjustments:
                adjustments[category.id] = {
                    'values': 0,
                    'numerators': 0,
                    'type_ids': []
                }
            adjustments[category.id]['values'] += ensure_decimal(
                disaggregation.get('value', 0))
            adjustments[category.id]['numerators'] += ensure_decimal(
                disaggregation.get('numerator', 0))
            adjustments[category.id]['type_ids'].append(type_id)
        for key, adjustment in adjustments.items():
            unmodifieds = Disaggregation.objects.filter(update=update, dimension_value__name=key)\
                .exclude(dimension_value__in=adjustment['type_ids'])\
                .aggregate(values=Sum('value'))
            total = adjustment['values'] + ensure_decimal(
                unmodifieds['values'])
            if numerator is not None and adjustment['numerators'] > numerator:
                raise serializers.ValidationError(
                    "The disaggregation numerator should not exceed update numerator"
                )
            if total > value:
                raise serializers.ValidationError(
                    "The accumulated disaggregations value should not exceed update value"
                )

    def is_valid(self, raise_exception=False):
        # HACK to allow nested posting...
        self._disaggregations_data = self.initial_data.pop(
            'disaggregations', [])
        super(IndicatorPeriodDataFrameworkSerializer,
              self).is_valid(raise_exception=raise_exception)