diff --git a/README.md b/README.md index 8dda46e4ee4b84337f72e219741225c1efb07957..5171fb50fcb78f7859de4ce83885daf82df60b2b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,25 @@ -sample python script for automatically depositing files to B2SHARE v2 +python script for (more or less) automatically depositing files to B2SHARE v2 -still work in progress +###### + +python run.py --apitoken $TOKEN --filenames <file_a.txt,file_b.txt> + +--b2shareurl (change the current default b2share url fsd-cloud9.zam.kfa-juelich.de) + +--community $id (e.g. e9b9792e-79fb-4b07-b6b4-b9c2bd06d095) + +--debug (to get some debug output. ATTENTION: tokens are shown due to python requests) + +--filenames + +A comma separated list of files to upload. URLs are also supported, at least +for openstack swift streaming worked well. +If you want to rename a file showing up in the deposit, write +filename_a::newfilename_a (use two double points) + +--finalize (whether to finalize the deposit, not always possible without additional metadata) + +--insecure (whether to use a insecure API endpoint) + +# streaming from http resource to b2share should work for http/https resources +python run.py --apitoken $TOKEN --filenames https://swift.zam.kfa-juelich.de:8889/v1/AUTH_acf10a23f9af48ec961c5354888cc212/public/unicore-servers-7.4.0.tgz diff --git a/depositor/b2share.py b/depositor/b2share.py index 1fd484c3e7cbb595ed5fcdaad44a3ca39029789a..ea65312e5ea747094df50206394a5a2bfe922932 100644 --- a/depositor/b2share.py +++ b/depositor/b2share.py @@ -1,8 +1,8 @@ """a b2share depositor.""" from furl import furl + import json import logging -import pprint import requests import sys @@ -35,18 +35,32 @@ class Depositor: self.community = self.select_community(communitynames, communityids) deposit_id, upload_url = self.create_deposit() - logging.debug("Deposit has id {} and files should be " - "uploaded to {}".format(deposit_id, upload_url)) self.upload_files(self.filenames, upload_url) + finalized = self.finalize_deposit() + if finalized: + output = 'Deposit available at: {}{}'.format( + self.b2shareurl.replace('api/', 'records/'), deposit_id) + else: + output = 'Deposit available to finalize at: {}{}/edit' \ + .format(self.b2shareurl.replace('api/', 'records/'), deposit_id) + logging.info('{}'.format(output)) def create_deposit(self): """create initial deposit based on provided or selected community.""" headers = {'Content-Type': 'application/json'} metadata = {'community': self.community} + while True: - title = user_input("Please enter the title of your deposit: ") - if title: - break + try: + title = user_input("Please enter the title of your deposit: ") + if title: + break + except EOFError: + logging.info("Exiting...") + exit(0) + except KeyboardInterrupt: + logging.info("Exiting...") + exit(0) metadata.update({'title': title.strip()}) while True: input_open_access = user_input("Grant open access? [Y/n] ").strip() @@ -63,9 +77,9 @@ class Depositor: r = requests.post(self.b2shareurl + "records/", params=self.url_params, data=json.dumps(metadata), headers=headers, verify=self.insecure_ssl) - self.deposit = r.json()['id'] - self.upload_url = r.json()['links']['files'] - return self.deposit, self.upload_url + deposit_id = r.json()['id'] + upload_url = r.json()['links']['files'] + return deposit_id, upload_url def fetch_community_list(self): """fetch a list of current communities from b2share.""" @@ -82,7 +96,8 @@ class Depositor: i += 1 return communitynames, communityids - def select_community(self, communitynames, communityids): + @classmethod + def select_community(cls, communitynames, communityids): """select a community from current b2share communities.""" for i in range(0, len(communitynames)): print('{int:2d}: '.format(int=i) + communitynames[i]) @@ -95,6 +110,9 @@ class Depositor: except EOFError: logging.info("Exiting...") exit(0) + except KeyboardInterrupt: + logging.info("Exiting...") + exit(0) else: if selected_community < 0 or selected_community >= \ len(communitynames): @@ -105,22 +123,40 @@ class Depositor: break return communityids[selected_community] - def finalize_deposit(self): + @classmethod + def finalize_deposit(cls): """close (publish) the deposit.""" - pass + return False def upload_files(self, filenames, upload_url): """upload files, start with one file.""" headers = {'Content-Type': 'application/octet-stream'} for filename in filenames: - try: - with open(filename, 'rb') as f: - requests.put(upload_url + '/' + filename, - params=self.url_params, data=f, - headers=headers, verify=self.insecure_ssl) - except IOError: - logging.error("File with name '{}' does not exist, " - "skipping".format(filename)) + if '::' in filename: + logging.info(filename.count('::')) + if not filename.count('::') is 1: + logging.error('Too many "::" for filename renaming of {} ' + 'given, skipping.'.format(filename)) + continue + source, target = filename.split('::') + else: + source = target = filename + + if source.startswith('https') or source.startswith('http'): + logging.debug('Streaming http/s object') + r = requests.get(source, stream=True) + requests.put(upload_url + '/' + target, + params=self.url_params, data=r, + headers=headers, verify=self.insecure_ssl) + else: + try: + with open(filename, 'rb') as f: + requests.put(upload_url + '/' + target, + params=self.url_params, data=f, + headers=headers, verify=self.insecure_ssl) + except IOError: + logging.error("File with name '{}' does not exist, " + "skipping".format(source)) def sanitize_api_url(url): @@ -129,7 +165,7 @@ def sanitize_api_url(url): :type url: string """ if not url.startswith('http'): - url = 'http://'+url + url = 'http://' + url b2share_url = furl(url) if 'api' not in str(b2share_url.path): b2share_url.path.add('api/')