Manage file added again to same session, data is updated.#21
Manage file added again to same session, data is updated.#21
Conversation
See #PARAF-359
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughRe-adding a file UID within the same session now updates the existing session entry (filename/title) instead of creating a duplicate. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/imio/esign/tests/test_utils.py`:
- Around line 465-475: The test currently calls add_files_to_session(signers,
(annex_uid,)) twice without supplying the existing session_id, so it may create
a new session instead of updating the same one; modify the test to capture the
returned sid from the first call, pass that sid back as the session_id argument
on the second add_files_to_session(...) invocation, then assert that the
returned sid is unchanged (identity) and that session["files"] still has length
1 with updated filename and title; reference add_files_to_session and the
session_id parameter when making the change.
In `@src/imio/esign/utils.py`:
- Around line 86-99: When replacing an already-added UID the code increments
session size before removing the old file, so remove_files_from_session()
subtracts the current annex size and leaves a stale session["size"]; to fix,
detect the existing UID in the block where annot['uids'].get(uid) == session_id,
obtain the old file size (e.g., from session["files"] entry or annex metadata
for that UID) and subtract that old size from session["size"] (or
call/remove_files_from_session in a way that uses the stored old size) before
adding/incrementing the new file size; update the logic around
remove_files_from_session, session["files"] and session["size"] (referencing
annot, uid, session, remove_files_from_session, existing_files, old_filename) so
session["size"] reflects the true post-replacement total.
- Around line 95-98: The code assumes a matching entry for uid exists in
session["files"] and directly indexes [0], causing a crash if the mapping is
stale; update the block that computes old_filename (the list comprehension using
session["files"] and uid) to safely search for the file (e.g., use next(...)
with a default None or check length of the filtered list) and only call
existing_files.remove(old_filename) and remove_files_from_session([uid], ...)
when a match is found; if no match is found, skip removing old_filename and
either log a warning or proceed to call remove_files_from_session with the uid
as needed to avoid raising an IndexError.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c53b70c0-9d70-4bb0-a1c1-9b7e3a2904d0
📒 Files selected for processing (4)
CHANGES.rstsrc/imio/esign/tests/test_utils.pysrc/imio/esign/utils.pytest-4.3.cfg
src/imio/esign/tests/test_utils.py
Outdated
| sid, session = add_files_to_session(signers, (annex_uid,)) | ||
| self.assertEqual(len(session["files"]), 1) | ||
| self.assertEqual(session["files"][0]["filename"], "annex0.pdf") | ||
| self.assertEqual(session["files"][0]["title"], "Annex 0") | ||
| # edit annex and add again, still one annex in session and data are updated | ||
| annex.file.filename = u"new_filename.pdf" | ||
| annex.setTitle('New title') | ||
| sid, session = add_files_to_session(signers, (annex_uid,)) | ||
| self.assertEqual(len(session["files"]), 1) | ||
| self.assertEqual(session["files"][0]["filename"], "new_filename.pdf") | ||
| self.assertEqual(session["files"][0]["title"], "New title") |
There was a problem hiding this comment.
Test doesn’t strictly prove “same session” update behavior.
The second call omits session_id, so this can still pass if a new session is created. Also, sid from Line 472 is unused. Force the same-session path and assert identity/count explicitly.
💡 Proposed fix
- sid, session = add_files_to_session(signers, (annex_uid,))
+ sid2, session = add_files_to_session(signers, (annex_uid,), session_id=sid)
+ self.assertEqual(sid2, sid)
+ self.assertEqual(len(annot["sessions"]), 1)
+ self.assertEqual(annot["uids"][annex_uid], sid)
self.assertEqual(len(session["files"]), 1)
self.assertEqual(session["files"][0]["filename"], "new_filename.pdf")
self.assertEqual(session["files"][0]["title"], "New title")Based on learnings: In src/imio/esign/utils.py, when an explicit session_id is provided to add_files_to_session(), discriminators are intentionally bypassed.
🧰 Tools
🪛 Ruff (0.15.4)
[warning] 472-472: Unpacked variable sid is never used
Prefix it with an underscore or any other dummy variable pattern
(RUF059)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/imio/esign/tests/test_utils.py` around lines 465 - 475, The test
currently calls add_files_to_session(signers, (annex_uid,)) twice without
supplying the existing session_id, so it may create a new session instead of
updating the same one; modify the test to capture the returned sid from the
first call, pass that sid back as the session_id argument on the second
add_files_to_session(...) invocation, then assert that the returned sid is
unchanged (identity) and that session["files"] still has length 1 with updated
filename and title; reference add_files_to_session and the session_id parameter
when making the change.
| old_filename = path.splitext([fn for fn in session["files"] | ||
| if fn['uid'] == uid][0]['filename'])[0] | ||
| existing_files.remove(old_filename) | ||
| remove_files_from_session([uid], remove_empty_session=False) |
There was a problem hiding this comment.
Prevent crash when UID mapping is stale.
This lookup assumes the UID always exists in session["files"]; if annotations drift, ...[0] raises and aborts the operation. Guard the lookup before dereferencing.
💡 Proposed fix
- old_filename = path.splitext([fn for fn in session["files"]
- if fn['uid'] == uid][0]['filename'])[0]
+ old_file = next((fn for fn in session["files"] if fn["uid"] == uid), None)
+ if old_file is None:
+ logger.error(
+ "File UID %s mapped to session %s but missing from session files.",
+ uid, session_id,
+ )
+ continue
+ old_filename = path.splitext(old_file["filename"])[0]🧰 Tools
🪛 Ruff (0.15.4)
[warning] 95-96: Prefer next(...) over single element slice
Replace with next(...)
(RUF015)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/imio/esign/utils.py` around lines 95 - 98, The code assumes a matching
entry for uid exists in session["files"] and directly indexes [0], causing a
crash if the mapping is stale; update the block that computes old_filename (the
list comprehension using session["files"] and uid) to safely search for the file
(e.g., use next(...) with a default None or check length of the filtered list)
and only call existing_files.remove(old_filename) and
remove_files_from_session([uid], ...) when a match is found; if no match is
found, skip removing old_filename and either log a warning or proceed to call
remove_files_from_session with the uid as needed to avoid raising an IndexError.
…me is not renamed if adding same file again to a session
There was a problem hiding this comment.
♻️ Duplicate comments (3)
src/imio/esign/tests/test_utils.py (1)
465-478:⚠️ Potential issue | 🟠 MajorMake the test explicitly target “same session” updates.
Line 472 and Line 477 re-add without
session_id, so this can still pass if a different session is selected. Forcesession_id=sidand assert session identity/count.Proposed fix
- sid, session = add_files_to_session(signers, (annex_uid,)) + sid, session = add_files_to_session(signers, (annex_uid,)) self.assertEqual(len(session["files"]), 1) @@ - sid, session = add_files_to_session(signers, (annex_uid,)) + sid2, session = add_files_to_session(signers, (annex_uid,), session_id=sid) + self.assertEqual(sid2, sid) + self.assertEqual(len(annot["sessions"]), 1) + self.assertEqual(annot["uids"][annex_uid], sid) self.assertEqual(len(session["files"]), 1) @@ - sid, session = add_files_to_session(signers, (annex_uid,)) + sid3, session = add_files_to_session(signers, (annex_uid,), session_id=sid) + self.assertEqual(sid3, sid) self.assertEqual(len(session["files"]), 1)Based on learnings: In
src/imio/esign/utils.py, when an explicitsession_idis provided toadd_files_to_session(), all discriminators are intentionally bypassed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/imio/esign/tests/test_utils.py` around lines 465 - 478, The test is re-adding files without forcing the same session so it may pass using a different session; modify the calls to add_files_to_session to pass the existing session id (use session_id=sid) when re-adding the annex and when adding the exact same file, and add an assertion that the returned session id equals the original sid to ensure the function updated the same session (refer to add_files_to_session and the sid variable and session_id parameter).src/imio/esign/utils.py (2)
90-93:⚠️ Potential issue | 🟠 MajorGuard UID lookup before dereferencing list index.
At Line 90,
[...][0]can raiseIndexErrorifannot["uids"]says the UID is in the session butsession["files"]is out of sync.Proposed fix
- old_filename = path.splitext([fn for fn in session["files"] - if fn['uid'] == uid][0]['filename'])[0] - existing_files.remove(old_filename) - remove_files_from_session([uid], remove_empty_session=False) + old_file = next((fn for fn in session["files"] if fn["uid"] == uid), None) + if old_file is None: + logger.error( + "File UID %s mapped to session %s but missing from session files.", + uid, session_id, + ) + continue + old_filename = path.splitext(old_file["filename"])[0] + if old_filename in existing_files: + existing_files.remove(old_filename) + remove_files_from_session([uid], remove_empty_session=False)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/imio/esign/utils.py` around lines 90 - 93, The current lookup for old_filename dereferences the zero index of a list comprehension which can raise IndexError if session["files"] is out of sync; modify the code that computes old_filename (the expression using session["files"] and uid) to safely find the matching file (e.g., use next(...) with a default None or check the length of the list before indexing), bail out or log and skip removal if no match is found, and ensure remove_files_from_session([uid], ...) is only called when a matching filename was located; refer to the uid variable, session["files"], the old_filename assignment, and remove_files_from_session to locate and fix the logic.
86-94:⚠️ Potential issue | 🟠 MajorReplacement flow can leave
session["size"]stale when file bytes changed.Line 80 adds incoming size before replacement, and Line 93 removal subtracts current annex size, not guaranteed previous stored size. Replacing same UID after file-content change can keep an incorrect total.
Proposed fix
- session["size"] = session.get("size", 0) + size + session["size"] = session.get("size", 0) + size + recompute_size = False @@ if annot['uids'].get(uid, -1) == session_id: @@ remove_files_from_session([uid], remove_empty_session=False) + recompute_size = True @@ if session["client_id"] is None: @@ if u"{session_id}" in session["title"]: session["title"] = session["title"].replace(u"{session_id}", str(session_id)) + if recompute_size: + session["size"] = sum(get_filesize(fdic["uid"]) for fdic in session["files"])🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/imio/esign/utils.py` around lines 86 - 94, The replacement branch that handles annot['uids'][uid] == session_id updates session["size"] incorrectly by adding the incoming size and later subtracting the current annex size; instead, fetch the previous file entry from session["files"] (the same list comprehension used to get old_filename) and read its stored size (e.g., old_entry['size'] or equivalent metadata) as old_size, then update session["size"] by subtracting old_size before adding the new incoming size (or replace by session["size"] += new_size - old_size); ensure you still remove the old_filename from existing_files and call remove_files_from_session([uid], remove_empty_session=False) after adjusting session["size"]; if the stored size is missing fall back to the annex/current size but prefer the stored value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/imio/esign/tests/test_utils.py`:
- Around line 465-478: The test is re-adding files without forcing the same
session so it may pass using a different session; modify the calls to
add_files_to_session to pass the existing session id (use session_id=sid) when
re-adding the annex and when adding the exact same file, and add an assertion
that the returned session id equals the original sid to ensure the function
updated the same session (refer to add_files_to_session and the sid variable and
session_id parameter).
In `@src/imio/esign/utils.py`:
- Around line 90-93: The current lookup for old_filename dereferences the zero
index of a list comprehension which can raise IndexError if session["files"] is
out of sync; modify the code that computes old_filename (the expression using
session["files"] and uid) to safely find the matching file (e.g., use next(...)
with a default None or check the length of the list before indexing), bail out
or log and skip removal if no match is found, and ensure
remove_files_from_session([uid], ...) is only called when a matching filename
was located; refer to the uid variable, session["files"], the old_filename
assignment, and remove_files_from_session to locate and fix the logic.
- Around line 86-94: The replacement branch that handles annot['uids'][uid] ==
session_id updates session["size"] incorrectly by adding the incoming size and
later subtracting the current annex size; instead, fetch the previous file entry
from session["files"] (the same list comprehension used to get old_filename) and
read its stored size (e.g., old_entry['size'] or equivalent metadata) as
old_size, then update session["size"] by subtracting old_size before adding the
new incoming size (or replace by session["size"] += new_size - old_size); ensure
you still remove the old_filename from existing_files and call
remove_files_from_session([uid], remove_empty_session=False) after adjusting
session["size"]; if the stored size is missing fall back to the annex/current
size but prefer the stored value.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8985022b-cd1a-4934-88c0-aca79edfe09d
📒 Files selected for processing (2)
src/imio/esign/tests/test_utils.pysrc/imio/esign/utils.py
There was a problem hiding this comment.
♻️ Duplicate comments (2)
src/imio/esign/utils.py (2)
90-92:⚠️ Potential issue | 🟠 MajorGuard old file lookup before dereferencing.
This assumes the UID is always present in
session["files"]; if mappings drift,[0]raises and aborts the update path.💡 Proposed fix
- old_filename = path.splitext([fn for fn in session["files"] - if fn['uid'] == uid][0]['filename'])[0] - existing_files.remove(old_filename) + old_file = next((fn for fn in session["files"] if fn["uid"] == uid), None) + if old_file is None: + logger.error( + "File UID %s mapped to session %s but missing from session files.", + uid, session_id, + ) + continue + old_filename = path.splitext(old_file["filename"])[0] + if old_filename in existing_files: + existing_files.remove(old_filename)#!/bin/bash # Verify unsafe indexed lookup is still present and inspect guard context. rg -n -C3 "old_filename = path\\.splitext\\(\\[fn for fn in session\\[\"files\"\\]" src/imio/esign/utils.py rg -n -C3 "if annot\\['uids'\\]\\.get\\(uid, -1\\) == session_id" src/imio/esign/utils.py🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/imio/esign/utils.py` around lines 90 - 92, The code assumes a matching file entry exists and indexes [0] directly when computing old_filename and then removes it from existing_files; change the lookup to safely search for the entry (e.g. use next(...) with a default None) and guard against None before calling path.splitext or existing_files.remove — if no entry is found, skip the removal/update path or log/raise a clear error; ensure you only call existing_files.remove(old_filename) when old_filename is defined and present in existing_files.
87-94:⚠️ Potential issue | 🟠 MajorSession size can stay stale when replacing an existing UID.
When updating a UID already present in the session, size is added up front, then removal subtracts the current annex size. If content size changed since prior add,
session["size"]can drift from real total and affect later auto-discrimination.💡 Proposed fix
- session["size"] = session.get("size", 0) + size + session["size"] = session.get("size", 0) + size + recompute_session_size = False @@ if annot['uids'].get(uid, -1) == session_id: @@ remove_files_from_session([uid], remove_empty_session=False) + recompute_session_size = True @@ annot["c_uids"].setdefault(context_uid, PersistentList()).append(uid) + if recompute_session_size: + session["size"] = sum(get_filesize(fdic["uid"]) for fdic in session["files"])🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/imio/esign/utils.py` around lines 87 - 94, The session size drifts when replacing a UID because the code adds the new content size then calls remove_files_from_session which subtracts the current annex size (not the original), so capture the original file size and adjust session["size"] using that original value instead of relying on remove_files_from_session to compute it. In the block guarded by if annot['uids'].get(uid, -1) == session_id: retrieve the original file record from session["files"] (the same place you get old_filename), read its stored size field (e.g., old_file.get('annex_size', 0) or old_file.get('size', 0)), subtract that original size from session["size"] (or pass it into remove_files_from_session so it uses the known old size) before adding the new file size and removing the old file; keep existing_files removal and calling remove_files_from_session but ensure you prevent double-size recalculation by either disabling size updates in remove_files_from_session (remove_empty_session=False already) or by updating remove_files_from_session to accept an old_size parameter and use it when provided.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/imio/esign/utils.py`:
- Around line 90-92: The code assumes a matching file entry exists and indexes
[0] directly when computing old_filename and then removes it from
existing_files; change the lookup to safely search for the entry (e.g. use
next(...) with a default None) and guard against None before calling
path.splitext or existing_files.remove — if no entry is found, skip the
removal/update path or log/raise a clear error; ensure you only call
existing_files.remove(old_filename) when old_filename is defined and present in
existing_files.
- Around line 87-94: The session size drifts when replacing a UID because the
code adds the new content size then calls remove_files_from_session which
subtracts the current annex size (not the original), so capture the original
file size and adjust session["size"] using that original value instead of
relying on remove_files_from_session to compute it. In the block guarded by if
annot['uids'].get(uid, -1) == session_id: retrieve the original file record from
session["files"] (the same place you get old_filename), read its stored size
field (e.g., old_file.get('annex_size', 0) or old_file.get('size', 0)), subtract
that original size from session["size"] (or pass it into
remove_files_from_session so it uses the known old size) before adding the new
file size and removing the old file; keep existing_files removal and calling
remove_files_from_session but ensure you prevent double-size recalculation by
either disabling size updates in remove_files_from_session
(remove_empty_session=False already) or by updating remove_files_from_session to
accept an old_size parameter and use it when provided.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1aa3aa45-79bc-4d05-a043-3181eb8a64d3
📒 Files selected for processing (2)
src/imio/esign/tests/test_utils.pysrc/imio/esign/utils.py
🚧 Files skipped from review as they are similar to previous changes (1)
- src/imio/esign/tests/test_utils.py
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/imio/esign/utils.py (1)
86-97:⚠️ Potential issue | 🔴 CriticalReplacing an existing UID still leaves
session["size"]double-counted.
session["size"]already includes the incoming file size before this block runs. Lines 95-97 then restore that inflated value afterremove_files_from_session(), so the old entry is never actually subtracted. The new regression insrc/imio/esign/tests/test_utils.pyshould expose this immediately: after Line 474, the size should stay6968, not jump to13936. This also skews later size-based session discrimination.💡 Suggested fix
if not session: session_id, session = create_session( signers, seal, acroform=acroform, title=title, annot=annot, discriminators=discriminators, watchers=watchers, create_session_custom_data=create_session_custom_data, ) session["size"] = session.get("size", 0) + size + recompute_size = False existing_files = [path.splitext(f["filename"])[0] for f in session["files"]] for uid in files_uids: annex = uuidToObject(uuid=uid, unrestricted=True) context_uid_provider = getAdapter(annex, IContextUidProvider) context_uid = context_uid_provider.get_context_uid() # update data if adding same file to same session if annot['uids'].get(uid, -1) == session_id: logger.info('File with UID %s is already in session_id %s and data were updated!', uid, session_id) # remove old filename to avoid filename being renamed old_filename = path.splitext([fn for fn in session["files"] if fn['uid'] == uid][0]['filename'])[0] existing_files.remove(old_filename) - # session size will be updated when removing file from session - # so save it because it is already correct - saved_size = session["size"] remove_files_from_session([uid], remove_empty_session=False) - session["size"] = saved_size + recompute_size = True @@ existing_files.append(new_filename) annot["uids"][uid] = session_id annot["c_uids"].setdefault(context_uid, PersistentList()).append(uid) + if recompute_size: + session["size"] = sum(get_filesize(fdic["uid"]) for fdic in session["files"]) if session["client_id"] is None:🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/imio/esign/utils.py` around lines 86 - 97, The session size is left double-counted because saved_size restores the pre-removal value which already included the incoming file; instead, after removing the old file call you should recalculate session["size"] from the remaining session["files"] (or subtract the removed file's size) rather than restoring saved_size. Modify the code around the UID-replacement branch (symbols: annot, uid, session_id, session, remove_files_from_session, existing_files, old_filename) so that after remove_files_from_session([uid], ...) you set session["size"] = sum(f['size'] for f in session["files"]) (or compute saved_size - removed_file_size) to ensure the size is correct.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/imio/esign/utils.py`:
- Around line 86-97: The session size is left double-counted because saved_size
restores the pre-removal value which already included the incoming file;
instead, after removing the old file call you should recalculate session["size"]
from the remaining session["files"] (or subtract the removed file's size) rather
than restoring saved_size. Modify the code around the UID-replacement branch
(symbols: annot, uid, session_id, session, remove_files_from_session,
existing_files, old_filename) so that after remove_files_from_session([uid],
...) you set session["size"] = sum(f['size'] for f in session["files"]) (or
compute saved_size - removed_file_size) to ensure the size is correct.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9e6bcb8e-ee0b-4758-8e2a-eeb194f554ef
📒 Files selected for processing (2)
src/imio/esign/tests/test_utils.pysrc/imio/esign/utils.py
…ted when annex updated
…to_same_session_again
… annex Adapted call to get_sessions_for to pass readonly=False as we update size when annex modified
|
|
||
| [sources] | ||
| collective.iconifiedcategory = git ${remotes:collective}/collective.iconifiedcategory.git pushurl=${remotes:collective_push}/collective.iconifiedcategory.git | ||
| imio.annex = git ${remotes:imio}/imio.annex.git pushurl=${remotes:imio_push}/imio.annex.git |
src/imio/esign/utils.py
Outdated
| return annot['sessions'][session_id] | ||
|
|
||
|
|
||
| def get_sessions_for(context_uid): |
There was a problem hiding this comment.
Fonction déjà définie dans main (avec paramètre readonly)
| # edit annex and add again, still one annex in session and data are updated | ||
| annex0.file.filename = u"new_annex0.pdf" | ||
| annex0.setTitle('New Annex 0') | ||
| sid, session = add_files_to_session(signers, (annex0_uid,)) |
There was a problem hiding this comment.
Est-ce qu'il ne faudrait pas tester ici que le filename et le title sont déjà mis à jour dans la session même avant de l'ajouter à nouveau ? Si oui, alors on n'est plus vraiment dans le contexte de test_utils mais plutôt test_events
There was a problem hiding this comment.
Je vais compléter test_categorized_element_updated_event avec changement file, çà teste que c'est bien màj lors d'un edit, et donc ici c'est la création, on a le postulat que dans ce cas c'est bien à jour, d'ailleurs on le voit dans ce test, sinon on re-teste des choses déjà testées... Il me semble...
There was a problem hiding this comment.
à voir car création ou ajout même annexe ne passe pas par l'event de modif, c'est juste le add_files_to_session qui gère cela, çà va mettre à jour les infos correctement, le code lié à l'event est ci-dessous dans le "edit file"...
Je vais push un test_categorized_element_updated_event + complet où on teste aussi que les infos filesize sont mises à jour, mais c'est double test pour moi
There was a problem hiding this comment.
@chris-adam en effet le test a évolué petit à petit, et la partie "add" après le "modify" a été enlevée pour montrer que c'est màj sans re-add, j'avais lu trop vite car ton commentaire était "trop haut" dans le test, ici çà me semble bon comme çà on pourrait merger à mon sens
See #PARAF-359
Summary by CodeRabbit
Bug Fixes
Tests
Documentation
Chores