def deposit_media(request, deposit): """ Deposit media endpoint: list, create, update or delete files Example GET of files list: curl -v http://127.0.0.1:8000/api/v1/file/149cc29d-6472-4bcf-bee8-f8223bf60580/sword/media/ Example PUT of file: curl -v -H "Content-Disposition: attachment; filename=joke.jpg" --data-binary "@joke.jpg" --request PUT http://127.0.0.1:8000/api/v1/file/9c8b4ac0-0407-4360-a10d-af6c62a48b69/sword/media/ Example POST of file: curl -v -H "Content-Disposition: attachment; filename=joke.jpg" --data-binary "@joke.jpg" --request POST http://127.0.0.1:8000/api/v1/file/9c8b4ac0-0407-4360-a10d-af6c62a48b69/sword/media/ Example POST of METS with file info: curl -v -H "Packaging: METS" -H "In-Progress: true" --data-binary @mets.xml --request POST http://127.0.0.1:8000/api/v1/file/9c8b4ac0-0407-4360-a10d-af6c62a48b69/sword/media/ Example DELETE of all files: curl -v -XDELETE http://127.0.0.1:8000/api/v1/file/9c8b4ac0-0407-4360-a10d-af6c62a48b69/sword/media/ Example DELETE of file: curl -v -XDELETE http://127.0.0.1:8000/api/v1/file/9c8b4ac0-0407-4360-a10d-af6c62a48b69/sword/media/?filename=joke.jpg """ if isinstance(deposit, basestring): try: deposit = models.Package.objects.get(uuid=deposit) except models.Package.DoesNotExist: return helpers.sword_error_response( request, 404, 'Deposit location {uuid} does not exist.'.format(uuid=deposit)) if deposit.has_been_submitted_for_processing(): return helpers.sword_error_response( request, 400, 'This deposit has already been submitted for processing.') if request.method == 'GET': # TODO should this be returned in SWORD XML? return HttpResponse(str(os.listdir(deposit.full_path))) elif request.method == 'PUT': # replace a file in the deposit return _handle_adding_to_or_replacing_file_in_deposit( request, deposit, replace_file=True) elif request.method == 'POST': # Allow async batch upload via METS XML body content if 'HTTP_PACKAGING' in request.META and request.META[ 'HTTP_PACKAGING'] == 'METS': # If METS XML has been sent to indicate a list of files needing downloading, handle it if request.body != '': temp_filepath = helpers.write_request_body_to_temp_file( request) try: mets_data = _parse_name_and_content_urls_from_mets_file( temp_filepath) except etree.XMLSyntaxError as e: os.unlink(temp_filepath) mets_data = None if mets_data != None: # move METS file to submission documentation directory object_id = mets_data.get('object_id', 'fedora') helpers.store_mets_data(temp_filepath, deposit, object_id) _spawn_batch_download_and_flag_finalization_if_requested( deposit, request, mets_data) return _deposit_receipt_response(request, deposit, 201) else: return helpers.sword_error_response( request, 412, 'Error parsing XML.') else: return helpers.sword_error_response( request, 400, 'No METS body content sent.') else: # add a file to the deposit return _handle_adding_to_or_replacing_file_in_deposit( request, deposit) elif request.method == 'DELETE': filename = request.GET.get('filename', '') if filename != '': # Delete PackageDownloadTaskFile for this filename models.PackageDownloadTaskFile.objects.filter( task__package=deposit).filter(filename=filename).delete() # Delete empty PackageDownloadTasks for this deposit models.PackageDownloadTask.objects.filter(package=deposit).filter( download_file_set=None).delete() # Delete file file_path = os.path.join(deposit.full_path, filename) if os.path.exists(file_path): os.remove(file_path) return HttpResponse(status=204) # No content else: return helpers.sword_error_response( request, 404, 'The path to this file (%s) does not exist.' % (file_path)) else: # Delete all PackageDownloadTaskFile and PackageDownloadTask for this deposit models.PackageDownloadTaskFile.objects.filter( task__package=deposit).delete() models.PackageDownloadTask.objects.filter(package=deposit).delete() # Delete all files for filename in os.listdir(deposit.full_path): filepath = os.path.join(deposit.full_path, filename) if os.path.isfile(filepath): os.remove(filepath) elif os.path.isdir(filepath): shutil.rmtree(filepath) return HttpResponse(status=204) # No content else: return helpers.sword_error_response( request, 405, 'This endpoint only responds to the GET, POST, PUT, and DELETE HTTP methods.' )
def collection(request, location): """ Collection endpoint: accepts deposits, and returns current deposits. Example GET of collection deposit list: curl -v http://localhost:8000/api/v1/location/96606387-cc70-4b09-b422-a7220606488d/sword/collection/ Example POST creation of deposit, allowing asynchronous downloading of object content URLs: curl -v -H "In-Progress: true" --data-binary @mets.xml --request POST http://localhost:8000/api/v1/location/96606387-cc70-4b09-b422-a7220606488d/sword/collection/ Example POST creation of deposit, finalizing the deposit and auto-approving it: curl -v -H "In-Progress: false" --data-binary @mets.xml --request POST http://localhost:8000/api/v1/location/c0bee7c8-3e9b-41e3-8600-ee9b2c475da2/sword/collection/ Example POST creation of deposit from another location: curl -v -H "In-Progress: true" --request POST http://localhost:8000/api/v1/location/96606387-cc70-4b09-b422-a7220606488d/sword/collection/?source_location=aab142a9-018f-4452-8f93-67c1bf7fd486&relative_path_to_files=archivematica-sampledata/SampleTransfers/Images """ if isinstance(location, basestring): try: location = models.Location.active.get(uuid=location) except models.Location.DoesNotExist: return helpers.sword_error_response( request, 404, 'Collection {uuid} does not exist.'.format(uuid=location)) if request.method == 'GET': # return list of deposits as ATOM feed col_iri = request.build_absolute_uri( reverse('sword_collection', kwargs={ 'api_name': 'v1', 'resource_name': 'location', 'uuid': location.uuid })) feed = {'title': 'Deposits', 'url': col_iri} entries = [] for deposit in helpers.deposit_list(location.uuid): edit_iri = request.build_absolute_uri( reverse('sword_deposit', kwargs={ 'api_name': 'v1', 'resource_name': 'file', 'uuid': deposit.uuid })) entries.append({ 'title': deposit.description or 'Deposit ' + deposit.uuid, 'url': edit_iri, }) # feed and entries passed via locals() to the template collection_xml = render_to_string('locations/api/sword/collection.xml', locals()) response = HttpResponse(collection_xml) response['Content-Type'] = 'application/atom+xml;type=feed' return response elif request.method == 'POST': # has the In-Progress header been set? if 'HTTP_IN_PROGRESS' in request.META: # process creation request, if criteria met source_location = request.GET.get('source_location', '') relative_path_to_files = request.GET.get('relative_path_to_files', '') if request.body != '': try: temp_filepath = helpers.write_request_body_to_temp_file( request) # parse name and content URLs out of XML try: mets_data = _parse_name_and_content_urls_from_mets_file( temp_filepath) except etree.XMLSyntaxError as e: os.unlink(temp_filepath) mets_data = None if mets_data != None: if mets_data['deposit_name'] == None: return helpers.sword_error_response( request, 400, 'No deposit name found in XML.') if not os.path.isdir(location.full_path): return helpers.sword_error_response( request, 500, 'Collection path (%s) does not exist: contact an administrator.' % (location.full_path)) # TODO: should get this from author header or provided XML metadata sourceofacquisition = request.META[ 'HTTP_ON_BEHALF_OF'] if 'HTTP_ON_BEHALF_OF' in request.META else None deposit = _create_deposit_directory_and_db_entry( location=location, deposit_name=mets_data['deposit_name'], sourceofacquisition=sourceofacquisition) if deposit is None: return helpers.sword_error_response( request, 500, 'Could not create deposit: contact an administrator.' ) # move METS file to submission documentation directory object_id = mets_data.get('object_id', 'fedora') helpers.store_mets_data(temp_filepath, deposit, object_id) _spawn_batch_download_and_flag_finalization_if_requested( deposit, request, mets_data) if request.META['HTTP_IN_PROGRESS'] == 'true': return _deposit_receipt_response( request, deposit, 201) else: return _deposit_receipt_response( request, deposit, 200) else: return helpers.sword_error_response( request, 412, 'Error parsing XML') except Exception as e: return helpers.sword_error_response( request, 400, traceback.format_exc()) elif source_location or relative_path_to_files: if not source_location or not relative_path_to_files: if not source_location: return helpers.sword_error_response( request, 400, 'relative_path_to_files is set, but source_location is not.' ) else: return helpers.sword_error_response( request, 400, 'source_location is set, but relative_path_to_files is not.' ) else: result = deposit_from_location_relative_path( source_location, relative_path_to_files, location) if result.get('error', False): return helpers.sword_error_response( request, 500, result['message']) else: return _deposit_receipt_response( request, result['deposit_uuid'], 200) else: return helpers.sword_error_response( request, 412, 'A request body must be sent when creating a deposit.') else: return helpers.sword_error_response( request, 412, 'The In-Progress header must be set to either true or false when creating a deposit.' ) else: return helpers.sword_error_response( request, 405, 'This endpoint only responds to the GET and POST HTTP methods.')
def deposit_edit(request, deposit): """ Deposit endpoint: list info, accept new files, finalize or delete deposit. Example POST adding files to the deposit: curl -v -H "In-Progress: true" --data-binary @mets.xml --request POST http://127.0.0.1:8000/api/v1/file/149cc29d-6472-4bcf-bee8-f8223bf60580/sword/ Example POST finalization of deposit: curl -v -H "In-Progress: false" --request POST http://127.0.0.1:8000/api/v1/file/149cc29d-6472-4bcf-bee8-f8223bf60580/sword/ Example DELETE of deposit: curl -v -XDELETE http://127.0.0.1:8000/api/v1/file/149cc29d-6472-4bcf-bee8-f8223bf60580/sword/ """ if isinstance(deposit, basestring): try: deposit = models.Package.objects.get(uuid=deposit) except models.Package.DoesNotExist: return helpers.sword_error_response( request, 404, 'Deposit location {uuid} does not exist.'.format(uuid=deposit)) if deposit.has_been_submitted_for_processing(): return helpers.sword_error_response( request, 400, 'This deposit has already been submitted for processing.') if request.method == 'GET': edit_iri = request.build_absolute_uri( reverse('sword_deposit', kwargs={ 'api_name': 'v1', 'resource_name': 'file', 'uuid': deposit.uuid })) entry = {'title': deposit.description, 'url': edit_iri} response = HttpResponse( render_to_string('locations/api/sword/entry.xml', locals())) response['Content-Type'] = 'application/atom+xml' return response elif request.method == 'POST': # If METS XML has been sent to indicate a list of files needing downloading, handle it if request.body != '': temp_filepath = helpers.write_request_body_to_temp_file(request) try: mets_data = _parse_name_and_content_urls_from_mets_file( temp_filepath) except etree.XMLSyntaxError as e: os.unlink(temp_filepath) mets_data = None if mets_data is not None: # move METS file to submission documentation directory object_id = mets_data.get('object_id', 'fedora') helpers.store_mets_data(temp_filepath, deposit, object_id) _spawn_batch_download_and_flag_finalization_if_requested( deposit, request, mets_data) return _deposit_receipt_response(request, deposit, 200) else: return helpers.sword_error_response(request, 412, 'Error parsing XML.') else: # Attempt to finalize (if requested), otherwise just return deposit receipt if 'HTTP_IN_PROGRESS' in request.META and request.META[ 'HTTP_IN_PROGRESS'] == 'false': return _finalize_or_mark_for_finalization(request, deposit) else: return _deposit_receipt_response(request, deposit, 200) elif request.method == 'PUT': # TODO: implement update deposit return HttpResponse(status=204) # No content elif request.method == 'DELETE': # Delete files shutil.rmtree(deposit.full_path) # Delete all PackageDownloadTaskFile and PackageDownloadTask for this deposit models.PackageDownloadTaskFile.objects.filter( task__package=deposit).delete() models.PackageDownloadTask.objects.filter(package=deposit).delete() # TODO should this actually delete the Package entry? deposit.status = models.Package.DELETED deposit.save() return HttpResponse(status=204) # No content else: return helpers.sword_error_response( request, 405, 'This endpoint only responds to the GET, POST, PUT, and DELETE HTTP methods.' )