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): """ Get the resource's download contents. Parameters ---------- ticket_number: str The ticket number of the download being prepared. Returns ------- 200: OK Returns the zip of resources to be downloaded. 400: Bad Request { "error": "'eaasi_token' not found as query parameter." } 401: Unauthorized { "PresQT Error: Header 'eaasi_token' does not match the 'eaasi_token' for this server process." } 404: Not Found { "message": "File unavailable." } """ try: token = request.query_params['eaasi_token'] except MultiValueDictKeyError: return Response( data={'error': "'eaasi_token' not found as query parameter."}, status=status.HTTP_400_BAD_REQUEST) # Perform token validation. Read data from the process_info file. try: data = get_process_info_data('downloads', ticket_number) process_token_validation(token, data, 'eaasi_token') except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) # Return the file to download if it has finished. if data['status'] == 'finished': # 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={'message': 'File unavailable.'}, status=status.HTTP_404_NOT_FOUND) return response
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": "ticket_number is missing from the request body." } """ try: ticket_number = request.data['ticket_number'] except KeyError: return Response(data={"error": "ticket_number is missing from the request body."}, status=status.HTTP_400_BAD_REQUEST) # Create a one time use token for EaaSI to use. eaasi_token = str(uuid4()) data = get_process_info_data('downloads', ticket_number) data['eaasi_token'] = eaasi_token write_file('mediafiles/downloads/{}/process_info.json'.format(ticket_number), 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 upload_get(self): """ Get the status of an upload job. """ # Perform token validation. Read data from the process_info file. try: destination_token = get_destination_token(self.request) self.ticket_number = 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: upload_process_data = self.process_data['resource_upload'] except KeyError: return Response(data={ 'error': 'PresQT Error: "resource_upload" not found in process_info file.' }, status=status.HTTP_400_BAD_REQUEST) upload_status = upload_process_data['status'] total_files = upload_process_data['upload_total_files'] files_finished = upload_process_data['upload_files_finished'] job_percentage = calculate_job_percentage(total_files, files_finished) data = { 'status_code': upload_process_data['status_code'], 'status': upload_status, 'message': upload_process_data['message'], 'job_percentage': job_percentage } if upload_status == 'finished': http_status = status.HTTP_200_OK data['status'] = upload_status data['failed_fixity'] = upload_process_data['failed_fixity'] data['resources_ignored'] = upload_process_data[ 'resources_ignored'] data['resources_updated'] = upload_process_data[ 'resources_updated'] data['link_to_resource'] = upload_process_data['link_to_resource'] data['job_percentage'] = 99 else: if upload_status == 'in_progress': http_status = status.HTTP_202_ACCEPTED data['job_percentage'] = job_percentage else: http_status = status.HTTP_500_INTERNAL_SERVER_ERROR return Response(status=http_status, data=data)
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 patch(self, request, ticket_number): """ Cancel the resource upload process on the server. Update the process_info.json file appropriately. Parameters ---------- ticket_number : str The ticket number of the upload being prepared. Returns ------- 200: OK { "status_code": "499", "message": "Upload was cancelled by the user" } 400: Bad Request { "error": "'presqt-destination-token' missing in the request headers." } 401: Unauthorized { "error": "Header 'presqt-destination-token' does not match the 'presqt-destination-token' for this server process." } 404: Not Found { "error": "Invalid ticket number, '1234'." } 406: Not Acceptable { "status_code": "200", "message": "Upload Successful" } """ # Perform token validation. Read data from the process_info file. try: token = get_destination_token(request) data = get_process_info_data('uploads', ticket_number) process_token_validation(hash_tokens(token), data, 'presqt-destination-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 upload while data['function_process_id'] is None: try: data = get_process_info_data('uploads', ticket_number) except json.decoder.JSONDecodeError: # Pass while the process_info file is being written to pass # If upload 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'] = 'Upload was cancelled by the user' data['status_code'] = '499' data['expiration'] = str(timezone.now() + relativedelta(hours=1)) process_info_path = 'mediafiles/uploads/{}/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 upload 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 get(self, request, ticket_number): """ Check in on the resource's upload process state. Parameters ---------- ticket_number : str The ticket number of the upload being prepared. Returns ------- 200: OK { "status_code": "200", "message": "Upload successful", "failed_fixity": [], "resources_ignored": [], "resources_updated": [] } 202: Accepted { "status_code": null, "message": "Upload is being processed on the server" } 400: Bad Request { "error": "PresQT Error: 'presqt-destination-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." } 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_destination_token(request) process_data = get_process_info_data('uploads', ticket_number) process_token_validation(hash_tokens(token), process_data, 'presqt-destination-token') except PresQTValidationError as e: return Response(data={'error': e.data}, status=e.status_code) upload_status = process_data['status'] data = { 'status_code': process_data['status_code'], 'message': process_data['message'] } if upload_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 upload_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 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 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)