def get_json_format(self): """ Retrieve details about a specific Resource. Returns ------- Response object in JSON format """ # Perform token, target, and action validation try: token = get_source_token(self.request) target_validation(self.source_target_name, self.action) except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) # Fetch the proper function to call func = FunctionRouter.get_function(self.source_target_name, self.action) # Fetch the resource try: resource = func(token, self.source_resource_id) except PresQTResponseException as e: # Catch any errors that happen within the target fetch return Response(data={'error': e.data}, status=e.status_code) serializer = ResourceSerializer(instance=resource, context={ 'target_name': self.source_target_name, 'request': self.request }) return Response(serializer.data)
def transfer_patch(self): """ Attempt to cancel a transfer job. """ # Perform token validation. Read data from the process_info file. try: destination_token = get_destination_token(self.request) source_token = get_source_token(self.request) self.ticket_number = '{}_{}'.format(hash_tokens(source_token), hash_tokens(destination_token)) process_data = get_process_info_data(self.ticket_number) except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) # Wait until the spawned off process has started to cancel the transfer while process_data['resource_transfer_in'][ 'function_process_id'] is None: try: process_data = get_process_info_data(self.ticket_number) except json.decoder.JSONDecodeError: # Pass while the process_info file is being written to pass transfer_process_data = process_data['resource_transfer_in'] # If transfer is still in progress then cancel the subprocess if transfer_process_data['status'] == 'in_progress': for process in multiprocessing.active_children(): if process.pid == transfer_process_data['function_process_id']: process.kill() process.join() transfer_process_data['status'] = 'failed' transfer_process_data[ 'message'] = 'Transfer was cancelled by the user' transfer_process_data['status_code'] = '499' transfer_process_data['expiration'] = str(timezone.now() + relativedelta( hours=1)) update_or_create_process_info(transfer_process_data, 'resource_transfer_in', self.ticket_number) return Response(data={ 'status_code': transfer_process_data['status_code'], 'message': transfer_process_data['message'] }, status=status.HTTP_200_OK) # If transfer is finished then don't attempt to cancel subprocess else: return Response(data={ 'status_code': transfer_process_data['status_code'], 'message': transfer_process_data['message'] }, status=status.HTTP_406_NOT_ACCEPTABLE)
def get(self, request, ticket_number): """ Check in on the resource's transfer process state. Parameters ---------- ticket_number : str The ticket number of the transfer being prepared. Returns ------- 200: OK """ # Perform token validation. Read data from the process_info file. try: destination_token = get_destination_token(request) source_token = get_source_token(request) process_data = get_process_info_data('transfers', ticket_number) process_token_validation(hash_tokens(destination_token), process_data, 'presqt-destination-token') process_token_validation(hash_tokens(source_token), process_data, 'presqt-source-token') except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) transfer_status = process_data['status'] data = {'status_code': process_data['status_code'], 'message': process_data['message']} if transfer_status == 'finished': http_status = status.HTTP_200_OK data['failed_fixity'] = process_data['failed_fixity'] data['resources_ignored'] = process_data['resources_ignored'] data['resources_updated'] = process_data['resources_updated'] else: if transfer_status == 'in_progress': http_status = status.HTTP_202_ACCEPTED else: http_status = status.HTTP_500_INTERNAL_SERVER_ERROR return Response(status=http_status, data=data)
def transfer_post(self): """ Transfer resources to a specific existing target resource or create a new target resource. Returns ------- Response object in JSON format """ try: self.destination_token = get_destination_token(self.request) self.source_token = get_source_token(self.request) self.email = get_user_email_opt(self.request) self.file_duplicate_action = file_duplicate_action_validation( self.request) self.keyword_action = keyword_action_validation(self.request) self.fairshare_evaluator_action = fairshare_evaluator_validation( self.request) self.source_target_name, self.source_resource_id, self.keywords = transfer_post_body_validation( self.request) target_valid, self.infinite_depth = target_validation( self.destination_target_name, self.action) target_validation(self.source_target_name, 'resource_transfer_out') transfer_target_validation(self.source_target_name, self.destination_target_name) self.supports_keywords = get_keyword_support( self.source_target_name, self.destination_target_name) except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) # Generate ticket number self.ticket_number = '{}_{}'.format( hash_tokens(self.source_token), hash_tokens(self.destination_token)) ticket_path = os.path.join("mediafiles", "jobs", str(self.ticket_number)) self.ticket_path = os.path.join('mediafiles', 'jobs', str(self.ticket_number), 'transfer') # Create directory and process_info json file self.process_info_obj = { 'presqt-source-token': hash_tokens(self.source_token), 'presqt-destination-token': hash_tokens(self.destination_token), 'status': 'in_progress', 'expiration': str(timezone.now() + relativedelta(hours=5)), 'message': 'Transfer is being processed on the server', 'download_status': None, 'upload_status': None, 'status_code': None, 'function_process_id': None, 'upload_total_files': 0, 'upload_files_finished': 0, 'download_total_files': 0, 'download_files_finished': 0 } self.process_info_path = update_or_create_process_info( self.process_info_obj, self.action, self.ticket_number) self.base_directory_name = '{}_{}_transfer_{}'.format( self.source_target_name, self.destination_target_name, self.source_resource_id) # Remove any resources that already exist in this user's job directory if os.path.exists(self.ticket_path): for folder in next(os.walk(self.ticket_path))[1]: shutil.rmtree(os.path.join(self.ticket_path, folder)) # Spawn the transfer_resource method separate from the request server by using multiprocess. spawn_action_process(self, self._transfer_resource, self.action) reversed_url = reverse('job_status', kwargs={'action': 'transfer'}) transfer_hyperlink = self.request.build_absolute_uri(reversed_url) return Response(status=status.HTTP_202_ACCEPTED, data={ 'message': 'The server is processing the request.', 'transfer_job': transfer_hyperlink })
def get(self, request, target_name): """ Retrieve all Resources. Parameters ---------- target_name : str The string name of the Target resource to retrieve. Returns ------- 200 : OK A list-like JSON representation of all resources for the given Target and token. [ { "kind": "container", "kind_name": "folder", "id": "5cd9832cf244ec0021e5f245", "container": "cmn5z:osfstorage", "title": "Images", "links": [ { "name": "Detail", "link": "https://localhost/api_v1/targets/osf/resources/5cd9832cf244ec0021e5f245/", "method": "GET" } ] }, { "kind": "item", "kind_name": "file", "id": "5cd98510f244ec001fe5632f", "container": "5cd9832cf244ec0021e5f245", "title": "22776439564_7edbed7e10_o.jpg", "links": [ { "name": "Detail", "link": "https://localhost/api_v1/targets/osf/resources/5cd98510f244ec001fe5632f/", "method": "GET" } ] } ] 400: Bad Request { "error": "PresQT Error: 'new_target' does not support the action 'resource_collection'." } or { "error": "PresQT Error: 'presqt-source-token' missing in the request headers." } or { "error": "PresQT Error: The search query is not formatted correctly." } 401: Unauthorized { "error": "Token is invalid. Response returned a 401 status code."" } 404: Not Found { "error": "PresQT Error: 'bad_target' is not a valid Target name." } """ action = 'resource_collection' # Perform token, target, and action validation try: token = get_source_token(request) target_validation(target_name, action) except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) query_params = request.query_params # Validate the search query if there is one. if query_params != {}: try: search_validator(query_params) query_params_value = list(query_params.values())[0] if query_params_value.isspace() or query_params_value == '': # If title is empty, we want to only return user resources. query_params = {} except PresQTResponseException as e: # Catch any errors that happen within the search validation return Response(data={'error': e.data}, status=e.status_code) # Fetch the proper function to call func = FunctionRouter.get_function(target_name, action) # Fetch the target's resources try: resources = func(token, query_params) except PresQTResponseException as e: # Catch any errors that happen within the target fetch return Response(data={'error': e.data}, status=e.status_code) serializer = ResourcesSerializer(instance=resources, many=True, context={ 'target_name': target_name, 'request': request}) return Response(serializer.data)
def patch(self, request, ticket_number): """ Cancel the resource transfer process on the server. Update the process_info.json file appropriately. Parameters ---------- ticket_number : str The ticket number of the transfer being prepared. Returns ------- 200: OK { "status_code": "499", "message": "Transfer was cancelled by the user" } 400: Bad Request { "error": "PresQT Error: 'presqt-destination-token' missing in the request headers." } 400: Bad Request { "error": "PresQT Error: 'presqt-source-token' missing in the request headers." } 401: Unauthorized { "error": "PresQT Error: Header 'presqt-destination-token' does not match the 'presqt-destination-token' for this server process." } 401: Unauthorized { "error": "PresQT Error: Header 'presqt-source-token' does not match the 'presqt-source-token' for this server process." } 404: Not Found { "error": "PresQT Error: Invalid ticket number, '1234'." } 406: Not Acceptable { "status_code": "200", "message": "Transfer Successful" } """ # Perform token validation. Read data from the process_info file. try: token = get_destination_token(request) source_token = get_source_token(request) process_data = get_process_info_data('transfers', ticket_number) process_token_validation(hash_tokens(token), process_data, 'presqt-destination-token') process_token_validation(hash_tokens(source_token), process_data, 'presqt-source-token') except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) # Wait until the spawned off process has started to cancel the transfer while process_data['function_process_id'] is None: try: process_data = get_process_info_data('transfers', ticket_number) except json.decoder.JSONDecodeError: # Pass while the process_info file is being written to pass # If transfer is still in progress then cancel the subprocess if process_data['status'] == 'in_progress': for process in multiprocessing.active_children(): if process.pid == process_data['function_process_id']: process.kill() process.join() process_data['status'] = 'failed' process_data['message'] = 'Transfer was cancelled by the user' process_data['status_code'] = '499' process_data['expiration'] = str(timezone.now() + relativedelta(hours=1)) process_info_path = 'mediafiles/transfers/{}/process_info.json'.format( ticket_number) write_file(process_info_path, process_data, True) return Response( data={'status_code': process_data['status_code'], 'message': process_data['message']}, status=status.HTTP_200_OK) # If transfer is finished then don't attempt to cancel subprocess else: return Response( data={'status_code': process_data['status_code'], 'message': process_data['message']}, status=status.HTTP_406_NOT_ACCEPTABLE)
def get(self, request, ticket_number, response_format=None): """ Check in on the resource's download process state. Parameters ---------- ticket_number : str The ticket number of the download being prepared. response_format: str The type of response to return. Either json or zip Returns ------- 200: OK Returns the zip of resources to be downloaded. or { "status_code": "200", "message": "Download successful but with fixity errors.", "failed_fixity": ["/Character Sheet - Alternative - Print Version.pdf"] } 202: Accepted { "status_code": null, "message": "Download is being processed on the server" } 400: Bad Request { "error": "PresQT Error: 'presqt-source-token' missing in the request headers." } or { "error": "PresQT Error: 'csv' is not a valid format for this endpoint." } 401: Unauthorized { "error": "PresQT Error: Header 'presqt-source-token' does not match the 'presqt-source-token' for this server process." } 404: Not Found { "error": "PresQT Error: Invalid ticket number, '1234'." } 500: Internal Server Error { "status_code": "404", "message": "Resource with id 'bad_id' not found for this user." } """ # Perform token validation. Read data from the process_info file. try: token = get_source_token(request) data = get_process_info_data('downloads', ticket_number) process_token_validation(hash_tokens(token), data, 'presqt-source-token') except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) # Verify that the only acceptable response format was provided if response_format and response_format not in ['json', 'zip']: return Response(data={ 'error': 'PresQT Error: {} is not a valid format for this endpoint.'. format(response_format) }, status=status.HTTP_400_BAD_REQUEST) download_status = data['status'] message = data['message'] status_code = data['status_code'] # Return the file to download if it has finished. if download_status == 'finished': if response_format == 'zip': # Path to the file to be downloaded zip_name = data['zip_name'] zip_file_path = os.path.join('mediafiles', 'downloads', ticket_number, zip_name) response = HttpResponse(open(zip_file_path, 'rb'), content_type='application/zip') response[ 'Content-Disposition'] = 'attachment; filename={}'.format( zip_name) else: response = Response(data={ 'status_code': status_code, 'message': message, 'zip_name': data['zip_name'], 'failed_fixity': data['failed_fixity'] }, status=status.HTTP_200_OK) return response else: if download_status == 'in_progress': http_status = status.HTTP_202_ACCEPTED else: http_status = status.HTTP_500_INTERNAL_SERVER_ERROR return Response(status=http_status, data={ 'status_code': status_code, 'message': message })
def patch(self, request, ticket_number, response_format=None): """ Cancel the resource download process on the server. Update the process_info.json file appropriately. Parameters ---------- ticket_number : str The ticket number of the download being prepared. response_format: str For patch, response_format should be JSON or None Returns ------- 200: OK { "status_code": "499", "message": "Download was cancelled by the user" } 400: Bad Request { "error": "PresQT Error: 'presqt-source-token' missing in the request headers." } or { "error": "PresQT Error: 'zip' is not a valid format for this endpoint." } 401: Unauthorized { "error": "PresQT Error: Header 'presqt-source-token' does not match the 'presqt-source-token' for this server process." } 404: Not Found { "error": "PresQT Error: Invalid ticket number, '1234'." } 406: Not Acceptable { "status_code": "200", "message": "Download Successful" } """ # Perform token validation. Read data from the process_info file. try: token = get_source_token(request) data = get_process_info_data('downloads', ticket_number) process_token_validation(hash_tokens(token), data, 'presqt-source-token') except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) # Verify that the only acceptable response format was provided if response_format and response_format != 'json': return Response(data={ 'error': 'PresQT Error: {} is not a valid format for this endpoint.'. format(response_format) }, status=status.HTTP_400_BAD_REQUEST) # Wait until the spawned off process has started to cancel the download while data['function_process_id'] is None: try: data = get_process_info_data('downloads', ticket_number) except json.decoder.JSONDecodeError: # Pass while the process_info file is being written to pass # If download is still in progress then cancel the subprocess if data['status'] == 'in_progress': for process in multiprocessing.active_children(): if process.pid == data['function_process_id']: process.kill() process.join() data['status'] = 'failed' data['message'] = 'Download was cancelled by the user' data['status_code'] = '499' data['expiration'] = str(timezone.now() + relativedelta(hours=1)) process_info_path = 'mediafiles/downloads/{}/process_info.json'.format( ticket_number) write_file(process_info_path, data, True) return Response(data={ 'status_code': data['status_code'], 'message': data['message'] }, status=status.HTTP_200_OK) # If download is finished then don't attempt to cancel subprocess else: return Response(data={ 'status_code': data['status_code'], 'message': data['message'] }, status=status.HTTP_406_NOT_ACCEPTABLE)
def post(self, request): """ Upload a proposal task to EaaSI Returns ------- 200: OK { "id": "19", "message": "Proposal task was submitted." "proposal_link": "https://localhost/api_v1/services/eaasi/1/" } 400: Bad Request { "error": "PresQT Error: 'presqt-source-token' missing in the request headers." } or { "error": "PresQT Error: A download does not exist for this user on the server." } 404: Not Found { "error": "PresQT Error: Invalid ticket number, '1234'." } or { "error": "PresQT Error: A resource_download does not exist for this user on the server." } """ # Get the source token from the request, hash it to get the ticket_number, get the # process_info.json file connected with the ticket_number. try: source_token = get_source_token(self.request) ticket_number = hash_tokens(source_token) process_info_data = get_process_info_data(ticket_number) download_data = get_process_info_action(process_info_data, 'resource_download') except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) # Create a one time use token for EaaSI to use. eaasi_token = str(uuid4()) download_data['eaasi_token'] = eaasi_token write_file( 'mediafiles/jobs/{}/process_info.json'.format(ticket_number), process_info_data, True) # Build EaaSI download endpoint url eaasi_download_reverse = reverse( 'eaasi_download', kwargs={"ticket_number": ticket_number}) eaasi_download_url = request.build_absolute_uri(eaasi_download_reverse) final_eaasi_download_url = '{}?eaasi_token={}'.format( eaasi_download_url, eaasi_token) data = {"data_url": final_eaasi_download_url, "data_type": "bagit+zip"} response = requests.post( 'https://eaasi-portal.emulation.cloud/environment-proposer/api/v1/proposals', data=json.dumps(data), headers={"Content-Type": "application/json"}) if response.status_code != 202: return Response(data={ 'message': 'Proposal submission returned a status code of {}.'.format( response.status_code) }, status=response.status_code) response_json = response.json() # Add Proposal link to payload reverse_proposal_url = reverse( 'proposal', kwargs={"proposal_id": response_json['id']}) response_json['proposal_link'] = request.build_absolute_uri( reverse_proposal_url) return Response(data=response_json, status=status.HTTP_200_OK)
def download_get(self): """ Get the status of a download job. Will return either a json object or a file bytes depending on the 'resource_format' url parameter """ if self.request.query_params: try: # This check will run for the email links we generate self.ticket_number = self.request.query_params['ticket_number'] self.process_data = get_process_info_data(self.ticket_number) except (MultiValueDictKeyError, PresQTValidationError): return Response(data={ 'error': "'ticket_number' not found as query parameter or invalid 'ticket_number' provided." }, status=status.HTTP_400_BAD_REQUEST) else: # Perform token validation. Read data from the process_info file. try: source_token = get_source_token(self.request) self.ticket_number = hash_tokens(source_token) self.process_data = get_process_info_data(self.ticket_number) except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) # Verify that the only acceptable response format was provided. if self.response_format and self.response_format not in [ 'json', 'zip' ]: return Response(data={ 'error': 'PresQT Error: {} is not a valid format for this endpoint.'. format(self.response_format) }, status=status.HTTP_400_BAD_REQUEST) try: download_process_data = self.process_data['resource_download'] except KeyError: return Response(data={ 'error': 'PresQT Error: "resource_download" not found in process_info file.' }, status=status.HTTP_400_BAD_REQUEST) download_status = download_process_data['status'] message = download_process_data['message'] status_code = download_process_data['status_code'] total_files = download_process_data['download_total_files'] files_finished = download_process_data['download_files_finished'] download_job_percentage = calculate_job_percentage( total_files, files_finished) # Return the file to download if it has finished. if download_status == 'finished': if self.response_format == 'zip': # Path to the file to be downloaded zip_name = download_process_data['zip_name'] zip_file_path = os.path.join('mediafiles', 'jobs', self.ticket_number, 'download', zip_name) response = HttpResponse(open(zip_file_path, 'rb'), content_type='application/zip') response[ 'Content-Disposition'] = 'attachment; filename={}'.format( zip_name) else: response = Response(data={ 'status_code': status_code, 'message': message, 'zip_name': download_process_data['zip_name'], 'failed_fixity': download_process_data['failed_fixity'], 'job_percentage': download_job_percentage, 'status': download_status }, status=status.HTTP_200_OK) return response else: if download_status == 'in_progress': http_status = status.HTTP_202_ACCEPTED else: http_status = status.HTTP_500_INTERNAL_SERVER_ERROR return Response(status=http_status, data={ 'job_percentage': download_job_percentage, 'status': download_status, 'status_code': status_code, 'message': message })
def download_patch(self): """ Attempt to cancel a download job. """ # Perform token validation. Read data from the process_info file. try: source_token = get_source_token(self.request) self.ticket_number = hash_tokens(source_token) self.process_data = get_process_info_data(self.ticket_number) except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) # Verify that the only acceptable response format was provided if self.response_format and self.response_format != 'json': return Response(data={ 'error': 'PresQT Error: {} is not a valid format for this endpoint.'. format(self.response_format) }, status=status.HTTP_400_BAD_REQUEST) # Wait until the spawned off process has started to cancel the download while self.process_data['resource_download'][ 'function_process_id'] is None: try: self.process_data = get_process_info_data(self.ticket_number) except json.decoder.JSONDecodeError: # Pass while the process_info file is being written to pass download_process_data = self.process_data['resource_download'] # If download is still in progress then cancel the subprocess if download_process_data['status'] == 'in_progress': for process in multiprocessing.active_children(): if process.pid == download_process_data['function_process_id']: process.kill() process.join() download_process_data['status'] = 'failed' download_process_data[ 'message'] = 'Download was cancelled by the user' download_process_data['status_code'] = '499' download_process_data['expiration'] = str(timezone.now() + relativedelta( hours=1)) update_or_create_process_info(download_process_data, 'resource_download', self.ticket_number) return Response(data={ 'status_code': download_process_data['status_code'], 'message': download_process_data['message'] }, status=status.HTTP_200_OK) # If download is finished then don't attempt to cancel subprocess else: return Response(data={ 'status_code': download_process_data['status_code'], 'message': download_process_data['message'] }, status=status.HTTP_406_NOT_ACCEPTABLE)
def transfer_get(self): """ Get the status of a transfer job. """ # Perform token validation. Read data from the process_info file. try: destination_token = get_destination_token(self.request) source_token = get_source_token(self.request) self.ticket_number = '{}_{}'.format(hash_tokens(source_token), hash_tokens(destination_token)) self.process_data = get_process_info_data(self.ticket_number) except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) try: transfer_process_data = self.process_data['resource_transfer_in'] except KeyError: return Response(data={ 'error': 'PresQT Error: "resource_download" not found in process_info file.' }, status=status.HTTP_400_BAD_REQUEST) transfer_status = transfer_process_data['status'] upload_job_percentage = calculate_job_percentage( transfer_process_data['upload_total_files'], transfer_process_data['upload_files_finished']) download_job_percentage = calculate_job_percentage( transfer_process_data['download_total_files'], transfer_process_data['download_files_finished']) data = { 'status_code': transfer_process_data['status_code'], 'status': transfer_status, 'message': transfer_process_data['message'], 'job_percentage': round((upload_job_percentage + download_job_percentage) / 2), } if transfer_status == 'finished': http_status = status.HTTP_200_OK data['failed_fixity'] = transfer_process_data['failed_fixity'] data['resources_ignored'] = transfer_process_data[ 'resources_ignored'] data['resources_updated'] = transfer_process_data[ 'resources_updated'] data['enhanced_keywords'] = transfer_process_data[ 'enhanced_keywords'] data['initial_keywords'] = transfer_process_data[ 'initial_keywords'] data['source_resource_id'] = transfer_process_data[ 'source_resource_id'] data['destination_resource_id'] = transfer_process_data[ 'destination_resource_id'] data['fairshare_evaluation_results'] = transfer_process_data[ 'fairshare_evaluation_results'] data['link_to_resource'] = transfer_process_data[ 'link_to_resource'] data['job_percentage'] = 99 else: if transfer_status == 'in_progress': http_status = status.HTTP_202_ACCEPTED else: http_status = status.HTTP_500_INTERNAL_SERVER_ERROR return Response(status=http_status, data=data)
def transfer_post(self): """ Transfer resources to a specific existing target resource or create a new target resource. Returns ------- Response object in JSON format """ try: self.destination_token = get_destination_token(self.request) self.source_token = get_source_token(self.request) self.file_duplicate_action = file_duplicate_action_validation( self.request) self.source_target_name, self.source_resource_id = transfer_post_body_validation( self.request) target_valid, self.infinite_depth = target_validation( self.destination_target_name, self.action) target_validation(self.source_target_name, 'resource_transfer_out') transfer_target_validation(self.source_target_name, self.destination_target_name) except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) # Generate ticket number ticket_number = uuid4() self.ticket_path = os.path.join("mediafiles", "transfers", str(ticket_number)) # Create directory and process_info json file self.process_info_obj = { 'presqt-source-token': hash_tokens(self.source_token), 'presqt-destination-token': hash_tokens(self.destination_token), 'status': 'in_progress', 'expiration': str(timezone.now() + relativedelta(days=5)), 'message': 'Transfer is being processed on the server', 'download_status': None, 'upload_status': None, 'status_code': None, 'function_process_id': None } self.process_info_path = os.path.join(self.ticket_path, "process_info.json") write_file(self.process_info_path, self.process_info_obj, True) self.base_directory_name = '{}_{}_transfer_{}'.format( self.source_target_name, self.destination_target_name, self.source_resource_id) # Spawn the transfer_resource method separate from the request server by using multiprocess. spawn_action_process(self, self._transfer_resource) reversed_url = reverse('transfer_job', kwargs={'ticket_number': ticket_number}) transfer_hyperlink = self.request.build_absolute_uri(reversed_url) return Response(status=status.HTTP_202_ACCEPTED, data={ 'ticket_number': ticket_number, 'message': 'The server is processing the request.', 'transfer_job': transfer_hyperlink })
def get_zip_format(self): """ Prepares a download of a resource with the given resource ID provided. Spawns a process separate from the request server to do the actual downloading and zip-file preparation. Returns ------- Response object with ticket information. """ # Perform token, target, and action validation try: self.source_token = get_source_token(self.request) target_validation(self.source_target_name, self.action) except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) # Generate ticket number ticket_number = uuid4() self.ticket_path = os.path.join('mediafiles', 'downloads', str(ticket_number)) # Create directory and process_info json file self.process_info_obj = { 'presqt-source-token': hash_tokens(self.source_token), 'status': 'in_progress', 'expiration': str(timezone.now() + relativedelta(days=5)), 'message': 'Download is being processed on the server', 'status_code': None, 'function_process_id': None } self.process_info_path = os.path.join(str(self.ticket_path), 'process_info.json') write_file(self.process_info_path, self.process_info_obj, True) self.base_directory_name = '{}_download_{}'.format( self.source_target_name, self.source_resource_id) # Spawn the upload_resource method separate from the request server by using multiprocess. spawn_action_process(self, self._download_resource) # Get the download url for zip format reversed_url = reverse('download_job', kwargs={ 'ticket_number': ticket_number, 'response_format': 'zip' }) download_zip_hyperlink = self.request.build_absolute_uri(reversed_url) # Get the download url for json format reversed_url = reverse('download_job', kwargs={ 'ticket_number': ticket_number, 'response_format': 'json' }) download_json_hyperlink = self.request.build_absolute_uri(reversed_url) return Response(status=status.HTTP_202_ACCEPTED, data={ 'ticket_number': ticket_number, 'message': 'The server is processing the request.', 'download_job_zip': download_zip_hyperlink, 'download_job_json': download_json_hyperlink })
def get(self, request, target_name, resource_id): """ Retrieve all keywords of a given resource. Parameters ---------- target_name : str The string name of the Target resource to retrieve. resource_id : str The id of the Resource to retrieve. Returns ------- 200 : OK A dictionary containing the keywords of the resource. { "keywords": [ "eggs", "animal", "water" ], "enhanced_keywords": [ "animals", "Animals", "eggs", "EGG", "Electrostatic Gravity Gradiometer", "water", "Water", "DISORDERED SOLVENT", "aqua", "Wasser", "dihydrogen oxide", "OXYGEN ATOM", "oxidane", "water" ] } 400: Bad Request { "error": "{Target} {Resource Type} do not have keywords." } or { "error": "PresQT Error: 'new_target' does not support the action 'keywords'." } 401: Unauthorized { "error": "Token is invalid. Response returned a 401 status code."" } """ action = 'keywords' try: token = get_source_token(request) target_validation(target_name, action) except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) # Fetch the proper function to call func = FunctionRouter.get_function(target_name, action) # Fetch the resource keywords try: # Will return a dictionary with resource's keywords keywords = func(token, resource_id) except PresQTResponseException as e: # Catch any errors that happen return Response(data={'error': e.data}, status=e.status_code) # Call function which calls SciGraph for keyword suggestions. try: # Return a new keyword list and a final list. new_list_of_keywords, final_list_of_keywords = keyword_enhancer( keywords['keywords']) except PresQTResponseException as e: # Catch any errors that happen within the target fetch return Response(data={'error': e.data}, status=e.status_code) return Response(data={ "keywords": keywords['keywords'], "enhanced_keywords": new_list_of_keywords, "all_keywords": final_list_of_keywords }, status=status.HTTP_200_OK)
def post(self, request, target_name, resource_id): """ Upload suggested keywords to a given resource. Parameters ---------- target_name : str The string name of the Target resource to retrieve. resource_id : str The id of the Resource to retrieve. Returns ------- 202 : ACCEPTED A dictionary containing the keywords of the resource. { "keywords_added": [ "Animals", "aqua", "dihydrogen oxide", "DISORDERED SOLVENT", "EGG", "Electrostatic Gravity Gradiometer", "oxidane", "OXYGEN ATOM", "Wasser", "Water" ], "final_keywords": [ "animal", "Animals", "aqua", "dihydrogen oxide", "DISORDERED SOLVENT", "EGG", "eggs", "Electrostatic Gravity Gradiometer", "oxidane", "OXYGEN ATOM", "Wasser", "water", "Water" ] } 400: Bad Request { "error": "{Target} {Resource Type} do not have keywords." } or { "error": "PresQT Error: 'new_target' does not support the action 'keywords'." } or { "error": "PresQT Error: 'new_target' does not support the action 'keywords_upload'." } or { "error": "PresQT Error: keywords is missing from the request body." } or { "error": "PresQT Error: keywords must be in list format." } 401: Unauthorized { "error": "Token is invalid. Response returned a 401 status code."" } 500: Internal Server Error { "error": "PresQT Error: Error updating the PresQT metadata file on {Target}. Keywords have been added successfully." } Target Error { "error": "{Target} returned a {status_code} error trying to update keywords. } """ action = "keywords_upload" try: token = get_source_token(request) target_validation(target_name, action) target_validation(target_name, 'keywords') keywords = keyword_post_validation(request) except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) # Fetch the initial keyword function func = FunctionRouter.get_function(target_name, 'keywords') # Fetch the resource keywords try: # Will return a dictionary with resource's keywords initial_keywords = func(token, resource_id) except PresQTResponseException as e: # Catch any errors that happen return Response(data={'error': e.data}, status=e.status_code) # Add the keywords all_keywords = initial_keywords['keywords'] + keywords # Fetch the update keywords function func = FunctionRouter.get_function(target_name, action) # Function will upload new keywords to the selected resource try: # Will return a dictionary with the updated_keywords updated_keywords = func(token, resource_id, all_keywords) except PresQTResponseException as e: # Catch any errors that happen within the target fetch return Response(data={'error': e.data}, status=e.status_code) # Get ontologies for enhanced keywords ontologies = fetch_ontologies(keywords) # Build the metadata dictionary for this action. source_target_data = get_target_data(target_name) metadata_dict = { "allKeywords": updated_keywords['updated_keywords'], "actions": [{ 'id': str(uuid4()), 'details': 'PresQT Enhance Keywords in {}'.format( source_target_data['readable_name']), 'actionDateTime': str(timezone.now()), 'actionType': 'keyword_enhancement', 'sourceTargetName': target_name, 'destinationTargetName': target_name, 'sourceUsername': '******', 'destinationUsername': '******', 'keywords': { 'sourceKeywordsAdded': [], 'sourceKeywordsEnhanced': keywords, 'ontologies': ontologies, 'enhancer': 'scigraph' }, 'files': { 'created': [], 'updated': [], 'ignored': [] } }] } # Update the metadata file, or create a new one. metadata_func = FunctionRouter.get_function(target_name, 'metadata_upload') try: metadata_func(token, updated_keywords['project_id'], metadata_dict) except PresQTError: # Catch any errors that happen within the target fetch return Response(data={ 'error': "PresQT Error: Error updating the PresQT metadata file on {}. Keywords have been added successfully." .format(target_name) }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) return Response(data={ 'initial_keywords': initial_keywords, 'keywords_added': keywords, 'final_keywords': updated_keywords['updated_keywords'] }, status=status.HTTP_202_ACCEPTED)