1313 HTTPException ,
1414 Query ,
1515 Request ,
16+ Response ,
1617 status ,
1718)
1819from pydantic import parse_raw_as
@@ -332,21 +333,24 @@ async def put(
332333 )
333334
334335 try :
335- statements_ids_result = DATABASE_CLIENT .query_statements_by_ids ([statementId ])
336+ existing_statement = DATABASE_CLIENT .query_statements_by_ids ([statementId ])
336337 except BackendException as error :
337338 raise HTTPException (
338339 status_code = status .HTTP_500_INTERNAL_SERVER_ERROR ,
339340 detail = "xAPI statements query failed" ,
340341 ) from error
341342
342- if len (statements_ids_result ) > 0 :
343- # NB: LRS specification calls for performing a deep comparison of incoming
344- # statements and existing statements with the same ID.
345- # This seems too costly for performance and was not implemented for this POC.
346- raise HTTPException (
347- status_code = status .HTTP_409_CONFLICT ,
348- detail = "Statement already exists with the same ID" ,
349- )
343+ if existing_statement :
344+ # The LRS specification calls for deep comparison of duplicate statement ids.
345+ # In the case that the current statement is not an exact duplicate of the one
346+ # found in the database we return a 409, otherwise the usual 204.
347+ for existing in existing_statement :
348+ if statement_dict != existing ["_source" ]:
349+ raise HTTPException (
350+ status_code = status .HTTP_409_CONFLICT ,
351+ detail = "A different statement already exists with the same ID" ,
352+ )
353+ return
350354
351355 # For valid requests, perform the bulk indexing of all incoming statements
352356 try :
@@ -368,6 +372,7 @@ async def put(
368372async def post (
369373 statements : Union [LaxStatement , List [LaxStatement ]],
370374 background_tasks : BackgroundTasks ,
375+ response : Response ,
371376):
372377 """Stores a set of statements (or a single statement as a single member of a set).
373378
@@ -405,21 +410,44 @@ async def post(
405410 )
406411
407412 try :
408- statements_ids_result = DATABASE_CLIENT .query_statements_by_ids (statements_ids )
413+ existing_statements = DATABASE_CLIENT .query_statements_by_ids (statements_ids )
409414 except BackendException as error :
410415 raise HTTPException (
411416 status_code = status .HTTP_500_INTERNAL_SERVER_ERROR ,
412417 detail = "xAPI statements query failed" ,
413418 ) from error
414419
415- if len (statements_ids_result ) > 0 :
416- # NB: LRS specification calls for performing a deep comparison of incoming
417- # statements and existing statements with the same ID.
418- # This seems too costly for performance and was not implemented for this POC.
419- raise HTTPException (
420- status_code = status .HTTP_409_CONFLICT ,
421- detail = "Statements already exist with the same ID" ,
422- )
420+ # If there are duplicate statements, remove them from our id list and
421+ # dictionary for insertion. We will return the shortened list of ids below
422+ # so that consumers can derive which statements were inserted and which
423+ # were skipped for being duplicates.
424+ # See: https://github.com/openfun/ralph/issues/345
425+ if existing_statements :
426+ existing_ids = []
427+ for existing in existing_statements :
428+ existing_ids .append (existing ["_id" ])
429+
430+ # The LRS specification calls for deep comparison of duplicates. This
431+ # is done here. If they are not exactly the same, we raise an error.
432+ if statements_dict [existing ["_id" ]] != existing ["_source" ]:
433+ raise HTTPException (
434+ status_code = status .HTTP_409_CONFLICT ,
435+ detail = "Differing statements already exist with the same ID: "
436+ f"{ existing ['_id' ]} " ,
437+ )
438+
439+ # Overwrite our statements and ids with the deduplicated ones.
440+ statements_ids = [id for id in statements_ids if id not in existing_ids ]
441+
442+ statements_dict = {
443+ key : value
444+ for key , value in statements_dict .items ()
445+ if key in statements_ids
446+ }
447+
448+ if not statements_ids :
449+ response .status_code = status .HTTP_204_NO_CONTENT
450+ return
423451
424452 # For valid requests, perform the bulk indexing of all incoming statements
425453 try :
0 commit comments