示例#1
0
    def validate(self, data):
        """Validate the absence data.

        An absence should not be created on a public holiday or a weekend.

        :returns: The validated data
        :rtype:   dict
        """
        location = Employment.objects.for_user(
            data.get('user'),
            data.get('date')
        ).location

        if PublicHoliday.objects.filter(
            location_id=location.id,
            date=data.get('date')
        ).exists():
            raise ValidationError(
                'You can\'t create an absence on a public holiday'
            )

        workdays = [int(day) for day in location.workdays]
        if data.get('date').isoweekday() not in workdays:
            raise ValidationError('You can\'t create an absence on a weekend')

        return data
示例#2
0
    def validate(self, data):
        """
        Validate that verified by is only set by reviewer or superuser.

        Additionally make sure a report is cannot be verified_by if is still
        needs review.
        """

        user = self.context["request"].user
        current_verified_by = self.instance and self.instance.verified_by
        new_verified_by = data.get("verified_by")
        task = data.get("task") or self.instance.task
        review = data.get("review")

        if new_verified_by != current_verified_by:
            is_reviewer = (
                user.is_superuser or task.project.reviewers.filter(id=user.id).exists()
            )

            if not is_reviewer:
                raise ValidationError(_("Only reviewer may verify reports."))

            if new_verified_by is not None and new_verified_by != user:
                raise ValidationError(_("You may only verifiy with your own user"))

            if new_verified_by and review:
                raise ValidationError(
                    _("Report can't both be set as `review` and `verified`.")
                )
        return data
示例#3
0
    def validate(self, data):
        """Validate the employment as a whole.

        Ensure the end date is after the start date and there is only one
        active employment per user and there are no overlapping employments.

        :throws: django.core.exceptions.ValidationError
        :return: validated data
        :rtype:  dict
        """
        instance = self.instance
        start_date = data.get("start_date", instance and instance.start_date)
        end_date = data.get("end_date", instance and instance.end_date)
        if end_date and start_date >= end_date:
            raise ValidationError(
                _("The end date must be after the start date"))

        user = data.get("user", instance and instance.user)
        employments = models.Employment.objects.filter(user=user)
        # end date not set means employment is ending today
        end_date = end_date or date.today()
        employments = employments.annotate(
            end=Coalesce("end_date", Value(date.today())))
        if instance:
            employments = employments.exclude(id=instance.id)

        if any([
                e.start_date <= end_date and start_date <= e.end
                for e in employments
        ]):
            raise ValidationError(
                _("A user can't have multiple employments at the same time"))

        return data
示例#4
0
    def validate(self, data):
        """Validate the absence data.

        An absence should not be created on a public holiday or a weekend.

        :returns: The validated data
        :rtype:   dict
        """
        instance = self.instance
        user = data.get("user", instance and instance.user)
        try:
            location = Employment.objects.get_at(user,
                                                 data.get("date")).location
        except Employment.DoesNotExist:
            raise ValidationError(
                _("You can't create an absence on an unemployed day."))

        if PublicHoliday.objects.filter(location_id=location.id,
                                        date=data.get("date")).exists():
            raise ValidationError(
                _("You can't create an absence on a public holiday"))

        workdays = [int(day) for day in location.workdays]
        if data.get("date").isoweekday() not in workdays:
            raise ValidationError(
                _("You can't create an absence on a weekend"))

        return data
示例#5
0
    def validate(self, data):
        """Validate the activity block.

        Ensure that a user can only have one activity
        which doesn't end before it started.
        """
        instance = self.instance
        from_time = data.get("from_time", instance and instance.from_time)
        to_time = data.get("to_time", instance and instance.to_time)
        user = instance and instance.user or data["user"]

        def validate_running_activity():
            if activity.filter(to_time__isnull=True).exists():
                raise ValidationError(
                    _("A user can only have one active activity"))

        # validate that there is only one active activity
        activity = models.Activity.objects.filter(user=user)
        # if the request creates a new activity
        if instance is None and to_time is None:
            validate_running_activity()
        # if the request mutates an existsting activity
        if instance and instance.to_time and to_time is None:
            validate_running_activity()

        # validate that to is not before from
        if to_time is not None and to_time < from_time:
            raise ValidationError(
                _("An activity block may not end before it starts."))

        return data
示例#6
0
    def _validate_owner_only(self, value, field):
        if self.instance is not None:
            user = self.context["request"].user
            owner = self.instance.user
            if getattr(self.instance, field) != value and user != owner:
                raise ValidationError(_(f"Only owner may change {field}"))

        return value
示例#7
0
def reward_amount_matches(data):
    """
    Validates that the reward activity is the same as the donation activity
    """
    if data.get('reward') and data['reward'].amount > data['amount']:
        raise ValidationError(
            _('The amount must be higher or equal to the amount of the reward.')
        )
示例#8
0
    def validate_type(self, value):
        """Only owner is allowed to change type."""
        if self.instance is not None:
            user = self.context["request"].user
            owner = self.instance.user
            if self.instance.date != value and user != owner:
                raise ValidationError(_("Only owner may change absence type"))

        return value
示例#9
0
 def __call__(self, data):
     activity = data.get('activity') or self.activity
     for field in self.fields:
         if (
             activity.target and
             field in data and
             data[field].currency != activity.target.currency
         ):
             raise ValidationError(self.message)
示例#10
0
    def validate_duration(self, value):
        """Only owner is allowed to change duration."""
        if self.instance is not None:
            user = self.context['request'].user
            owner = self.instance.user
            if self.instance.duration != value and user != owner:
                raise ValidationError('Only owner may change duration')

        return value
示例#11
0
    def validate(self, data):
        """Validate that verified by is only set by reviewer or superuser."""
        user = self.context['request'].user
        current_verified_by = self.instance and self.instance.verified_by
        new_verified_by = data.get('verified_by')
        task = data.get('task') or self.instance.task

        if new_verified_by != current_verified_by:
            is_reviewer = (user.is_superuser or
                           task.project.reviewers.filter(id=user.id).exists())

            if not is_reviewer:
                raise ValidationError(_('Only reviewer may verify reports.'))

            if new_verified_by is not None and new_verified_by != user:
                raise ValidationError(
                    _('You may only verifiy with your own user'))

        return data
示例#12
0
    def validate(self, data):
        """Validate the activity block.

        Ensure that a user can only have one activity with an active block
        which doesn't end before it started.
        """
        instance = self.instance
        from_time = data.get('from_time', instance and instance.from_time)
        to_time = data.get('to_time', instance and instance.to_time)
        user = instance and instance.activity.user or data.get('activity').user

        # validate that there is only one active activity
        blocks = models.ActivityBlock.objects.filter(activity__user=user)
        if blocks.filter(to_time__isnull=True) and to_time is None:
            raise ValidationError(
                _('A user can only have one active activity'))

        # validate that to is not before from
        if to_time is not None and to_time < from_time:
            raise ValidationError(
                _('An activity block may not end before it starts.'))

        return data
示例#13
0
    def validate_verified_by(self, value):
        user = self.context['request'].user
        current_verified_by = self.instance and self.instance.verified_by

        if value == current_verified_by:
            # no validation needed when nothing has changed
            return value

        if value is None and user.is_staff:
            # staff is allowed to reset verified by
            return value

        if value is not None and user.is_staff and value == user:
            # staff user is allowed to set it's own user as verified by
            return value

        raise ValidationError(_('Only staff user may verify reports.'))
示例#14
0
def filter_state(queryset, _, value):
    if not isinstance(value, list):
        value = [value]

    enum_values = []
    for item in value:
        try:
            enum_value = TransitState[item.upper()]
        except KeyError:
            raise ValidationError('Invalid shipment state supplied.')
        enum_values.append(enum_value.value)

    if issubclass(queryset.model, Shipment):
        queryset = queryset.filter(state__in=enum_values)
    else:
        queryset = queryset.filter(shipment__state__in=enum_values)

    return queryset
示例#15
0
    def validate(self, data):
        """Validate the activity block.

        Ensure that a user can only have one activity with an active block.
        """
        if self.instance:
            user = self.instance.activity.user
            to_datetime = data.get('to_datetime', self.instance.to_datetime)
        else:
            user = data.get('activity').user
            to_datetime = data.get('to_datetime', None)

        blocks = models.ActivityBlock.objects.filter(activity__user=user)

        if (blocks.filter(to_datetime__isnull=True) and to_datetime is None):
            raise ValidationError('A user can only have one active activity')

        return data
示例#16
0
 def __call__(self, data):
     if data.get(self.field) and not data[self.field].activity == data['activity']:
         raise ValidationError(self.message)
示例#17
0
 def validate_running_activity():
     if activity.filter(to_time__isnull=True).exists():
         raise ValidationError(
             _("A user can only have one active activity"))
示例#18
0
 def __call__(self, data):
     if data.get('user') and data['user'].is_authenticated and self.user and self.user != data['user']:
         raise ValidationError(self.message)
    def test_shipment_imports(self):

        url = reverse('import-shipments-list', kwargs={'version': 'v1'})

        csv_path = './tests/tmp/test.csv'
        xls_path = './tests/tmp/test.xls'
        xlsx_path = './tests/tmp/test.xlsx'

        self.make_shipment_file(csv_path)
        self.make_shipment_file(xls_path)
        self.make_shipment_file(xlsx_path)

        csv_file_data = {
            'name': 'Test csv file',
            'description': 'Auto generated file for test purposes',
            'file_type': 'csv',
            'storage_credentials_id': STORAGE_CRED_ID,
            'shipper_wallet_id': SHIPPER_WALLET_ID,
            'carrier_wallet_id': CARRIER_WALLET_ID
        }

        xls_file_data = copy.deepcopy(csv_file_data)
        xls_file_data['name'] = 'Test xls file'
        xls_file_data['file_type'] = 'Xls'
        xls_file_data['upload_status'] = 'complete'

        xlsx_file_data = copy.deepcopy(csv_file_data)
        xlsx_file_data['name'] = 'Test xlsx file'
        xlsx_file_data['file_type'] = '2'
        xlsx_file_data['processing_status'] = 'failed'

        # Unauthenticated user should fail
        response = self.client.post(url, csv_file_data)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

        self.set_user(self.user_1)

        with mock.patch('apps.imports.serializers.ShipmentImportCreateSerializer.validate_shipper_wallet_id') as mock_wallet_validation, \
                mock.patch('apps.imports.serializers.ShipmentImportCreateSerializer.validate_storage_credentials_id') as mock_storage_validation:

            mock_wallet_validation.return_value = SHIPPER_WALLET_ID
            mock_storage_validation.return_value = STORAGE_CRED_ID

            # --------------------- Upload csv file --------------------#
            # Authenticated request should succeed
            response = self.client.post(url, csv_file_data)
            self.assertEqual(response.status_code, status.HTTP_201_CREATED)
            data = response.json()['data']
            self.assertEqual(data['attributes']['upload_status'], 'PENDING')
            csv_obj = ShipmentImport.objects.get(id=data['id'])
            fields = data['meta']['presigned_s3']['fields']

            # csv file upload
            put_url = data['meta']['presigned_s3']['url']
            with open(csv_path, 'rb') as csv:
                res = requests.post(put_url, data=fields, files={'file': csv})

            self.assertEqual(res.status_code, status.HTTP_204_NO_CONTENT)
            self.assertTrue(self.s3_object_exists(csv_obj.s3_key))

            # --------------------- Upload xls file --------------------#
            response = self.client.post(url, xls_file_data)
            self.assertEqual(response.status_code, status.HTTP_201_CREATED)
            data = response.json()['data']
            # upload_status field is not configurable at creation
            self.assertEqual(data['attributes']['upload_status'], 'PENDING')
            self.assertEqual(data['attributes']['processing_status'],
                             'PENDING')
            # Wallets and storage should be present in response
            self.assertEqual(data['attributes'].get('storage_credentials_id'),
                             STORAGE_CRED_ID)
            self.assertEqual(data['attributes'].get('shipper_wallet_id'),
                             SHIPPER_WALLET_ID)
            self.assertEqual(data['attributes'].get('carrier_wallet_id'),
                             CARRIER_WALLET_ID)
            xls_obj = ShipmentImport.objects.get(id=data['id'])
            fields = data['meta']['presigned_s3']['fields']

            # xls file upload
            put_url = data['meta']['presigned_s3']['url']
            with open(xls_path, 'rb') as xls:
                res = requests.post(put_url, data=fields, files={'file': xls})

            self.assertEqual(res.status_code, status.HTTP_204_NO_CONTENT)
            self.assertTrue(self.s3_object_exists(xls_obj.s3_key))

            # --------------------- Upload xlsx file --------------------#
            response = self.client.post(url, xlsx_file_data)
            self.assertEqual(response.status_code, status.HTTP_201_CREATED)
            data = response.json()['data']
            self.assertEqual(data['attributes']['upload_status'], 'PENDING')
            # processing_status field is not configurable at creation
            self.assertEqual(data['attributes']['processing_status'],
                             'PENDING')
            xlsx_obj = ShipmentImport.objects.get(id=data['id'])
            fields = data['meta']['presigned_s3']['fields']

            # xlsx file upload
            put_url = data['meta']['presigned_s3']['url']
            with open(xlsx_path, 'rb') as xlsx:
                res = requests.post(put_url, data=fields, files={'file': xlsx})

            self.assertEqual(res.status_code, status.HTTP_204_NO_CONTENT)
            self.assertTrue(self.s3_object_exists(xlsx_obj.s3_key))

            # -------------------- test patch object -------------------#
            patch_csv_data = {
                'name': 'Can update name',
                'file_type': 'XLS',  # Cannot be modified
                'owner_id': 'new_owner_id',  # Cannot be modified
                'masquerade_id': 'new_masquerade_id',  # Cannot be modified
                "upload_status": "Complete",
                "processing_status": "Complete",
                "report": {
                    "success": 15,
                    "failed": 0
                }
            }
            csv_patch_url = f'{url}/{csv_obj.id}/'
            response = self.client.patch(csv_patch_url, data=patch_csv_data)
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            data = response.json()['data']
            # The file_type cannot change on patch
            self.assertEqual(data['attributes']['file_type'], 'CSV')
            self.assertEqual(data['attributes']['report'],
                             patch_csv_data['report'])
            self.assertEqual(data['attributes']['processing_status'],
                             'COMPLETE')
            # Wallets and storage, should be present in the response object and are non modifiable
            self.assertEqual(data['attributes'].get('storage_credentials_id'),
                             STORAGE_CRED_ID)
            self.assertEqual(data['attributes'].get('shipper_wallet_id'),
                             SHIPPER_WALLET_ID)
            self.assertEqual(data['attributes'].get('carrier_wallet_id'),
                             CARRIER_WALLET_ID)
            # owner_id and masquerade_id shouldn't be present in the response object
            self.assertIsNone(data['attributes'].get('owner_id'))
            self.assertIsNone(data['attributes'].get('masquerade_id'))

            # wallet and storage are non modifiable fields
            new_wallet_id = 'Wallet_is_Non_Modifiable'
            mock_wallet_validation.reset_mock()
            mock_wallet_validation.return_value = new_wallet_id

            patch_csv_data['shipper_wallet_id'] = new_wallet_id

            response = self.client.patch(csv_patch_url, data=patch_csv_data)
            self.assertEqual(mock_wallet_validation.call_count, 0)
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            data = response.json()['data']
            # The shipper_wallet_id attribute cannot be patched
            self.assertEqual(data['attributes']['shipper_wallet_id'],
                             SHIPPER_WALLET_ID)
            csv_obj.refresh_from_db()
            self.assertEqual(csv_obj.shipper_wallet_id, SHIPPER_WALLET_ID)

            # ------------------ permissions test -----------------------#
            self.set_user(self.user_2)

            # user_2 can create an xls file object
            response = self.client.post(url, xlsx_file_data)
            self.assertEqual(response.status_code, status.HTTP_201_CREATED)

            # user_2 cannot access a document object not owned
            user_1_xlsx_url = f'{url}/{xlsx_obj.id}/'
            response = self.client.get(user_1_xlsx_url)
            self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

            # user_2 cannot modify a document object not owned
            user_1_xlsx_url = f'{url}/{xlsx_obj.id}/'
            response = self.client.patch(user_1_xlsx_url, data=patch_csv_data)
            self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

            # user_2 can list only document owned
            response = self.client.get(url)
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            data = response.json()['data']
            self.assertEqual(len(data), 1)
            user_2_csv_document = ShipmentImport.objects.get(id=data[0]['id'])
            self.assertEqual(user_2_csv_document.masquerade_id, self.user_2.id)

            mock_wallet_validation.reset_mock()

            # Trying to upload a document without a shipper wallet should fail
            csv_file_data_without_shipper_wallet = copy.deepcopy(csv_file_data)
            csv_file_data_without_shipper_wallet.pop('shipper_wallet_id')

            response = self.client.post(url,
                                        csv_file_data_without_shipper_wallet)
            self.assertEqual(mock_wallet_validation.call_count, 0)
            self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

            # Trying to upload a document with a wallet / storage not owned by the requester should fail
            error_message = 'User does not have access to this wallet in ShipChain Profiles'
            mock_wallet_validation.side_effect = ValidationError(error_message)
            mock_wallet_validation.return_value = None

            csv_file_data_with_invalid_shipper_wallet = copy.deepcopy(
                csv_file_data)
            csv_file_data_with_invalid_shipper_wallet[
                'shipper_wallet_id'] = 'Non_Accessible_Wallet'

            response = self.client.post(
                url, csv_file_data_with_invalid_shipper_wallet)
            self.assertEqual(mock_wallet_validation.call_count, 1)
            self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
            data = response.json()['errors'][0]
            self.assertEqual(data['detail'], error_message)

            # ------------------ filtering test -----------------------#
            self.set_user(self.user_1)
            # User_1 owns 3 ShipmentImport objects respectively of one file type

            response = self.client.get(f'{url}/?file_type=XLS')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            data = response.json()['data']
            self.assertEqual(len(data), 1)
            self.assertEqual(data[0]['attributes']['file_type'],
                             FileType.XLS.name)

            # User_1 only has the CSV file with upload status complete
            response = self.client.get(f'{url}/?upload_status=Complete')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            data = response.json()['data']
            self.assertEqual(len(data), 1)
            self.assertEqual(data[0]['attributes']['file_type'],
                             FileType.CSV.name)
            self.assertEqual(data[0]['attributes']['upload_status'],
                             UploadStatus.COMPLETE.name)

            # user_1 only has one shipment import with name: 'Test xlsx file'
            shipment_import_name = 'Test xlsx file'
            response = self.client.get(f'{url}/?name__contains=XLsx')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            data = response.json()['data']
            self.assertEqual(len(data), 1)
            self.assertEqual(data[0]['attributes']['file_type'],
                             FileType.XLSX.name)
            self.assertEqual(data[0]['attributes']['name'],
                             shipment_import_name)

            # User_1 only has the CSV file with processing status complete
            response = self.client.get(f'{url}/?processing_status=COMPLETE')
            self.assertEqual(response.status_code, status.HTTP_200_OK)
            data = response.json()['data']
            self.assertEqual(len(data), 1)
            self.assertEqual(data[0]['attributes']['file_type'],
                             FileType.CSV.name)
            self.assertEqual(data[0]['attributes']['processing_status'],
                             ProcessingStatus.COMPLETE.name)