def unlink_instrument_from_exercise(self, instrument_id, exercise_id, session=None):
        """
        Unlink a collection instrument and a collection exercise.  If there is a link between the exercise
        and a business, this will also be removed.

        :param instrument_id: A collection instrument id (UUID)
        :param exercise_id: A collection exercise id (UUID)
        :param session: database session
        :return: True if instrument has been successfully unlinked to exercise
        """
        bound_logger = log.bind(instrument_id=instrument_id, exercise_id=exercise_id)
        bound_logger.info('Unlinking instrument and exercise')

        instrument = self.get_instrument_by_id(instrument_id, session)
        exercise = self.get_exercise_by_id(exercise_id, session)
        if not instrument or not exercise:
            bound_logger.info('Failed to unlink, unable to find instrument or exercise')
            raise RasError('Unable to find instrument or exercise', 404)

        instrument.exercises.remove(exercise)
        for business in instrument.businesses:
            bound_logger.info("Removing business/exercise link", business_id=business.id, ru_ref=business.ru_ref)
            business.instruments.remove(instrument)

        if not self.publish_remove_collection_instrument(exercise_id, instrument.instrument_id):
            raise RasError('Failed to publish upload message', 500)

        bound_logger.info('Successfully unlinked instrument to exercise')
        return True
    def upload_instrument_with_no_collection_exercise(self, survey_id, classifiers=None, session=None):
        """
        Upload a collection instrument to the db without a collection exercise

        :param classifiers: Classifiers associated with the instrument
        :param session: database session
        :param survey_id: database session
        :return: a collection instrument instance
        """

        log.info('Upload instrument', survey_id=survey_id)

        validate_uuid(survey_id)
        instrument = InstrumentModel(ci_type='EQ')

        survey = self._find_or_create_survey_from_survey_id(survey_id, session)
        instrument.survey = survey

        if classifiers:
            deserialized_classifiers = loads(classifiers)
            instruments = self._get_instruments_by_classifier(deserialized_classifiers, None, session)
            for instrument in instruments:
                if instrument.classifiers == deserialized_classifiers:
                    raise RasError("Cannot upload an instrument with an identical set of classifiers", 400)
            instrument.classifiers = deserialized_classifiers

        session.add(instrument)

        return instrument
def service_request(service, endpoint, search_value):
    """
    Makes a request to a different micro service

    :param service: The micro service to call to
    :param endpoint: The end point of the micro service
    :param search_value: The value to search on
    :return: response
    """

    auth = (current_app.config.get('SECURITY_USER_NAME'),
            current_app.config.get('SECURITY_USER_PASSWORD'))

    try:
        service = {
            'survey-service':
            current_app.config['SURVEY_URL'],
            'collectionexercise-service':
            current_app.config['COLLECTION_EXERCISE_URL'],
            'case-service':
            current_app.config['CASE_URL'],
            'party-service':
            current_app.config['PARTY_URL']
        }[service]
        service_url = f'{service}/{endpoint}/{search_value}'
        log.info(f'Making request to {service_url}')
    except KeyError:
        raise RasError(f"service '{service}' not configured", 500)

    response = requests.get(service_url, auth=auth)
    response.raise_for_status()
    return response
def patch_collection_instrument(instrument_id):
    file = request.files["file"]
    if file.filename == "":
        raise RasError("Missing filename", 400)

    CollectionInstrument().patch_seft_instrument(instrument_id, file)

    return make_response(PATCH_SUCCESSFUL, 200)
Ejemplo n.º 5
0
    def validate_non_duplicate_instrument(file, exercise_id, session):
        exercise = query_exercise_by_id(exercise_id, session)

        if exercise:
            for i in exercise.instruments:
                if i.seft_file.file_name == file.filename:
                    log.error("Collection instrument file already uploaded for this collection exercise")
                    raise RasError("Collection instrument file already uploaded for this collection exercise", 400)

        return
    def patch_seft_instrument(self, instrument_id: str, file, session):
        """
        Replaces the the seft_file for an instrument with the one provided.

        :param instrument_id: The top level instrument id that needs changing
        :param file: A FileStorage object with the new file
        :param session: A database session
        :raises RasError: Raised when instrument id is invalid, instrument not found, or instrument isn't of type SEFT
        """
        validate_uuid(instrument_id)
        instrument = self.get_instrument_by_id(instrument_id, session)
        if instrument is None:
            log.error('Instrument not found')
            raise RasError('Instrument not found', 400)
        if instrument.type != 'SEFT':
            log.error('Not a SEFT instrument')
            raise RasError('Not a SEFT instrument', 400)

        seft_model = self._update_seft_file(instrument.seft_file, file)
        session.add(seft_model)
def link_collection_instrument(instrument_id, exercise_id):
    CollectionInstrument().link_instrument_to_exercise(instrument_id,
                                                       exercise_id)
    response = publish_uploaded_collection_instrument(exercise_id,
                                                      instrument_id)
    if response.status_code != 200:
        log.error("Failed to publish upload message",
                  instrument_id=instrument_id,
                  collection_exercise_id=exercise_id)
        raise RasError("Failed to publish upload message", 500)
    return make_response(LINK_SUCCESSFUL, 200)
Ejemplo n.º 8
0
def validate_uuid(*values):
    """
    validate value is a uuid

    :param values: array of values to check
    :return: boolean
    """
    for value in values:
        try:
            UUID(value)
        except ValueError:
            raise RasError(f"Value is not a valid UUID ({value})", 400)
        return True
Ejemplo n.º 9
0
    def patch_seft_instrument(self, instrument_id: str, file, session):
        """
        Replaces the the seft_file for an instrument with the one provided.

        :param instrument_id: The top level instrument id that needs changing
        :param file: A FileStorage object with the new file
        :param session: A database session
        :raises RasError: Raised when instrument id is invalid, instrument not found, or instrument isn't of type SEFT
        """
        validate_uuid(instrument_id)
        instrument = self.get_instrument_by_id(instrument_id, session)
        if instrument is None:
            log.error("Instrument not found")
            raise RasError("Instrument not found", 400)
        if instrument.type != "SEFT":
            log.error("Not a SEFT instrument")
            raise RasError("Not a SEFT instrument", 400)

        survey_ref = get_survey_ref(instrument.survey.survey_id)
        exercise_id = str(instrument.exids[0])

        seft_model = self._update_seft_file(instrument.seft_file, file, survey_ref, exercise_id)
        session.add(seft_model)
def upload_collection_instrument(exercise_id, ru_ref=None):
    file = request.files["file"]
    classifiers = request.args.get("classifiers")
    instrument = CollectionInstrument().upload_to_bucket(
        exercise_id, file, ru_ref=ru_ref, classifiers=classifiers)

    if not publish_uploaded_collection_instrument(exercise_id,
                                                  instrument.instrument_id):
        log.error(
            "Failed to publish upload message",
            instrument_id=instrument.instrument_id,
            collection_exercise_id=exercise_id,
            ru_ref=ru_ref,
        )
        raise RasError("Failed to publish upload message", 500)
    return make_response(UPLOAD_SUCCESSFUL, 200)
 def upload_file_to_bucket(self, file):
     path = file.filename
     if self.prefix != "":
         path = self.prefix + "/" + path
     log.info("Uploading SEFT CI to GCP bucket: " + path)
     key = current_app.config.get("ONS_CRYPTOKEY", None)
     if key is None:
         log.error("Customer defined encryption key is missing.")
         raise RasError(
             "can't find customer defined encryption, hence can't perform this task",
             500)
     customer_supplied_encryption_key = sha256(key.encode("utf-8")).digest()
     blob = self.bucket.blob(
         blob_name=path, encryption_key=customer_supplied_encryption_key)
     blob.upload_from_file(file_obj=file.stream, rewind=True)
     log.info("Successfully put SEFT CI in bucket")
     return
    def _update_seft_file(seft_model, file):
        """
        Updates a seft_file with a new version of the data

        :param file: A file object from which we can read the file contents
        :return: instrument
        """
        log.info('Updating instrument seft file')
        file_contents = file.read()
        file_size = len(file_contents)
        if file_size == 0:
            raise RasError('File is empty', 400)
        cryptographer = Cryptographer()
        encrypted_file = cryptographer.encrypt(file_contents)
        seft_model.data = encrypted_file
        seft_model.length = file_size
        seft_model.file_name = file.filename
        return seft_model
 def download_file_from_bucket(self, file_location: str):
     if self.prefix != "":
         path = self.prefix + "/" + file_location
     else:
         path = file_location
     log.info("Downloading SEFT CI from GCP bucket: " + path)
     key = current_app.config.get("ONS_CRYPTOKEY", None)
     if key is None:
         log.error("Customer defined encryption key is missing.")
         raise RasError(
             "can't find customer defined encryption, hence can't perform this task",
             500)
     customer_supplied_encryption_key = sha256(key.encode("utf-8")).digest()
     blob = self.bucket.blob(
         blob_name=path, encryption_key=customer_supplied_encryption_key)
     file = blob.download_as_bytes()
     log.info("Successfully downloaded SEFT CI from GCP bucket")
     return file
Ejemplo n.º 14
0
    def test_ras_error_in_session(self):
        # Given an upload file and a patched survey_id response which returns a RasError
        data = {'file': (BytesIO(b'test data'), 'test.xls')}
        mock_survey_service = RasError('The service raised an error')

        with patch(
                'application.controllers.collection_instrument.service_request',
                side_effect=mock_survey_service):
            # When a post is made to the upload end point
            response = self.client.post(
                '/collection-instrument-api/1.0.2/upload/cb0711c3-0ac8-41d3-ae0e-567e5ea1ef87'
                '?classifiers={"form_type": "001"}',
                headers=self.get_auth_headers(),
                data=data,
                content_type='multipart/form-data')

        # Then a error is reported
        self.assertIn('The service raised an error', response.data.decode())
Ejemplo n.º 15
0
    def validate_one_instrument_for_ru_specific_upload(exercise, business, session):
        """
        Checks there hasn't been an instrument loaded for this reporting unit in this collection exercise already.

        The algorithm for this is as follows:
          - Check if this business_id is related to other instrument_id's.
          - If true, check each the exercise_id for each of those instruments to see if it matches the exercise we're
          attempting to add to.
          - If any match, then we're trying to add a second collection instrument for a reporting unit for this
          collection exercise and an exception will be raised.

        :param exercise: A db object representing the collection exercise
        :param business: A db object representing the business data
        :param session: A database session
        :raises RasError:  Raised when a duplicate is found

        """
        bound_logger = log.bind(ru_ref=business.ru_ref)
        bound_logger.info("Validating only one instrument per reporting unit per exercise")
        business = query_business_by_ru(business.ru_ref, session)
        if business:
            business_id = str(business.id)
            for instrument in business.instruments:
                instrument_id = str(instrument.id)
                bound_logger.bind(id_of_instrument=instrument_id, business_id=business_id)
                bound_logger.info("Reporting unit has had collection instruments uploaded for it in the past")

                for related_exercise in instrument.exercises:
                    related_exercise_id = related_exercise.exercise_id
                    exercise_id = exercise.exercise_id
                    bound_logger.bind(exercise_id=exercise_id, related_exercise_id=related_exercise_id)
                    bound_logger.info("About to check exercise for match")
                    if related_exercise_id == exercise_id:
                        bound_logger.info(
                            "Was about to add a second instrument for a reporting unit for a " "collection exercise"
                        )
                        ru_ref = business.ru_ref
                        error_text = (
                            f"Reporting unit {ru_ref} already has an instrument "
                            f"uploaded for this collection exercise"
                        )
                        raise RasError(error_text, 400)
Ejemplo n.º 16
0
    def _update_seft_file(seft_model, file, survey_ref, exercise_id):
        """
        Updates a seft_file with a new version of the data

        :param file: A file object from which we can read the file contents
        :return: instrument
        """
        log.info("Updating instrument seft file")
        file_contents = file.read()
        file_size = len(file_contents)
        if file_size == 0:
            raise RasError("File is empty", 400)
        seft_model.length = file_size
        old_filename = seft_model.file_name
        seft_model.file_name = file.filename
        file.filename = survey_ref + "/" + exercise_id + "/" + file.filename
        old_filename = survey_ref + "/" + exercise_id + "/" + old_filename
        seft_ci_bucket = GoogleCloudSEFTCIBucket(current_app.config)
        seft_ci_bucket.delete_file_from_bucket(old_filename)
        seft_ci_bucket.upload_file_to_bucket(file=file)
        seft_model.gcs = True
        return seft_model