66from urllib .parse import urljoin
77import aiofiles
88import aiohttp
9- import aiohttp_retry
109
1110from dvuploader .file import File
12- from dvuploader .nativeupload import file_sender
1311from dvuploader .utils import build_url
1412
1513TESTING = bool (os .environ .get ("DVUPLOADER_TESTING" , False ))
2422
2523TICKET_ENDPOINT = "/api/datasets/:persistentId/uploadurls"
2624ADD_FILE_ENDPOINT = "/api/datasets/:persistentId/addFiles"
27- UPLOAD_ENDPOINT = "/api/datasets/:persistentId/add ?persistentId="
28- REPLACE_ENDPOINT = "/api/files/{FILE_ID}/replace "
25+ UPLOAD_ENDPOINT = "/api/datasets/:persistentId/addFiles ?persistentId="
26+ REPLACE_ENDPOINT = "/api/datasets/:persistentId/replaceFiles?persistentId= "
2927
3028
3129async def direct_upload (
@@ -84,31 +82,20 @@ async def direct_upload(
8482 "x-amz-tagging" : "dv-state=temp" ,
8583 }
8684
87- connector = aiohttp .TCPConnector (limit = 10 , force_close = True )
88- pbar = progress .add_task ("╰── [bold white]Registering files" , total = len (files ))
89- results = []
85+ pbar = progress .add_task ("╰── [bold white]Registering files" , total = 1 )
86+ connector = aiohttp .TCPConnector (limit = 2 )
9087 async with aiohttp .ClientSession (
9188 headers = headers ,
9289 connector = connector ,
9390 ) as session :
94-
95- register_tasks = [
96- _add_file_to_ds (
97- session = session ,
98- file = file ,
99- dataverse_url = dataverse_url ,
100- pid = persistent_id ,
101- progress = progress ,
102- pbar = pbar ,
103- )
104- for file in files
105- ]
106-
107- results = await asyncio .gather (* register_tasks )
108-
109- for file , status in zip (files , results ):
110- if status is False :
111- print (f"❌ Failed to register file '{ file .fileName } ' at Dataverse" )
91+ await _add_files_to_ds (
92+ session = session ,
93+ files = files ,
94+ dataverse_url = dataverse_url ,
95+ pid = persistent_id ,
96+ progress = progress ,
97+ pbar = pbar ,
98+ )
11299
113100
114101async def _upload_to_store (
@@ -488,14 +475,14 @@ async def _abort_upload(
488475 response .raise_for_status ()
489476
490477
491- async def _add_file_to_ds (
478+ async def _add_files_to_ds (
492479 session : aiohttp .ClientSession ,
493480 dataverse_url : str ,
494481 pid : str ,
495- file : File ,
482+ files : List [ File ] ,
496483 progress ,
497484 pbar ,
498- ) -> bool :
485+ ) -> None :
499486 """
500487 Adds a file to a Dataverse dataset.
501488
@@ -508,38 +495,77 @@ async def _add_file_to_ds(
508495 Returns:
509496 bool: True if the file was added successfully, False otherwise.
510497 """
511- if not file .to_replace :
512- url = urljoin (dataverse_url , UPLOAD_ENDPOINT + pid )
513- else :
514- url = build_url (
515- dataverse_url = dataverse_url ,
516- endpoint = urljoin (
517- dataverse_url ,
518- REPLACE_ENDPOINT .format (FILE_ID = file .file_id ),
519- ),
520- )
521498
522- json_data = file .model_dump_json (
523- by_alias = True ,
524- exclude = {"to_replace" , "file_id" },
499+ novel_url = urljoin (dataverse_url , UPLOAD_ENDPOINT + pid )
500+ replace_url = urljoin (dataverse_url , REPLACE_ENDPOINT + pid )
501+
502+ novel_json_data = _prepare_registration (files , use_replace = False )
503+ replace_json_data = _prepare_registration (files , use_replace = True )
504+
505+ await _multipart_json_data_request (
506+ session = session ,
507+ json_data = novel_json_data ,
508+ url = novel_url ,
509+ )
510+
511+ await _multipart_json_data_request (
512+ session = session ,
513+ json_data = replace_json_data ,
514+ url = replace_url ,
515+ )
516+
517+ progress .update (pbar , advance = 1 )
518+
519+
520+ def _prepare_registration (files : List [File ], use_replace : bool ) -> str :
521+ """
522+ Prepares the files for registration at the Dataverse instance.
523+
524+ Args:
525+ files (List[File]): The list of files to prepare.
526+
527+ Returns:
528+ str: A JSON string containing the file data.
529+ """
530+
531+ exclude = {"to_replace" } if use_replace else {"to_replace" , "file_id" }
532+
533+ return json .dumps (
534+ [
535+ file .model_dump (
536+ by_alias = True ,
537+ exclude = exclude ,
538+ exclude_none = True ,
539+ )
540+ for file in files
541+ if file .to_replace is use_replace
542+ ],
525543 indent = 2 ,
526- exclude_none = True ,
527544 )
528545
546+
547+ async def _multipart_json_data_request (
548+ json_data : str ,
549+ url : str ,
550+ session : aiohttp .ClientSession ,
551+ ):
552+ """
553+ Sends a multipart/form-data POST request with JSON data to the specified URL using the provided session.
554+
555+ Args:
556+ json_data (str): The JSON data to be sent in the request body.
557+ url (str): The URL to send the request to.
558+ session (aiohttp.ClientSession): The aiohttp client session to use for the request.
559+
560+ Raises:
561+ aiohttp.ClientResponseError: If the response status code is not successful.
562+
563+ Returns:
564+ None
565+ """
529566 with aiohttp .MultipartWriter ("form-data" ) as writer :
530567 json_part = writer .append (json_data )
531568 json_part .set_content_disposition ("form-data" , name = "jsonData" )
532569
533- retry_options = aiohttp_retry .RandomRetry (attempts = MAX_RETRIES , statuses = {400 })
534- retry_client = aiohttp_retry .RetryClient (
535- client_session = session ,
536- retry_options = retry_options ,
537- )
538-
539- async with retry_client .post (url , data = writer ) as response :
540- if response .status == 200 :
541- progress .update (pbar , advance = 1 )
542- return True
543-
544- await retry_client .close ()
545- return False
570+ async with session .post (url , data = writer ) as response :
571+ response .raise_for_status ()
0 commit comments