From 1773ed9deeb60d442dcd3625590228374c15900a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 7 Apr 2026 13:26:39 +0100 Subject: [PATCH 1/5] CU-869ctq789: Add workaround for saving model pack instead of CDB --- medcat-trainer/webapp/api/api/views.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/medcat-trainer/webapp/api/api/views.py b/medcat-trainer/webapp/api/api/views.py index 5bb33b0fd..cf142a75a 100644 --- a/medcat-trainer/webapp/api/api/views.py +++ b/medcat-trainer/webapp/api/api/views.py @@ -579,7 +579,11 @@ def save_models(request): project = ProjectAnnotateEntities.objects.get(id=p_id) cat = get_medcat(project=project) - cat.cdb.save(project.concept_db.cdb_file.path) + if project.concept_db is not None: + # CDB / vocab based + cat.cdb.save(project.concept_db.cdb_file.path) + else: + cat.save_model_pack(project.model_pack.path) return Response({'message': 'Models saved'}) From c62dae82e25a95536033320c3bd195ff83974abf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 7 Apr 2026 13:29:41 +0100 Subject: [PATCH 2/5] CU-869ctq789: Fix issue with saving model to new name --- medcat-trainer/webapp/api/api/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/medcat-trainer/webapp/api/api/views.py b/medcat-trainer/webapp/api/api/views.py index cf142a75a..6ac077bda 100644 --- a/medcat-trainer/webapp/api/api/views.py +++ b/medcat-trainer/webapp/api/api/views.py @@ -583,7 +583,8 @@ def save_models(request): # CDB / vocab based cat.cdb.save(project.concept_db.cdb_file.path) else: - cat.save_model_pack(project.model_pack.path) + cat.save_model_pack(project.model_pack.path, + add_hash_to_pack_name=True) return Response({'message': 'Models saved'}) From 759101f4a3fb13a97a8c12643ec15573381310e1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 7 Apr 2026 13:31:16 +0100 Subject: [PATCH 3/5] CU-869ctq789: Allow CDB to overwritten when saving CDB/Vocab based project --- medcat-trainer/webapp/api/api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/medcat-trainer/webapp/api/api/views.py b/medcat-trainer/webapp/api/api/views.py index 6ac077bda..c2fa94553 100644 --- a/medcat-trainer/webapp/api/api/views.py +++ b/medcat-trainer/webapp/api/api/views.py @@ -581,7 +581,7 @@ def save_models(request): if project.concept_db is not None: # CDB / vocab based - cat.cdb.save(project.concept_db.cdb_file.path) + cat.cdb.save(project.concept_db.cdb_file.path, overwrite=True) else: cat.save_model_pack(project.model_pack.path, add_hash_to_pack_name=True) From 34b75397fae0540aba383252d3d739d94862316f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 7 Apr 2026 13:47:44 +0100 Subject: [PATCH 4/5] CU-869ctq789: Make model pack save more robust / flexible --- medcat-trainer/webapp/api/api/views.py | 29 +++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/medcat-trainer/webapp/api/api/views.py b/medcat-trainer/webapp/api/api/views.py index c2fa94553..a64a42aeb 100644 --- a/medcat-trainer/webapp/api/api/views.py +++ b/medcat-trainer/webapp/api/api/views.py @@ -1,8 +1,9 @@ import logging import os from smtplib import SMTPException -from tempfile import NamedTemporaryFile +from tempfile import NamedTemporaryFile, TemporaryDirectory from typing import Any +import shutil from background_task.models import Task, CompletedTask from django.contrib.auth.views import PasswordResetView @@ -578,13 +579,35 @@ def save_models(request): p_id = request.data['project_id'] project = ProjectAnnotateEntities.objects.get(id=p_id) cat = get_medcat(project=project) + from pathlib import Path + path = Path() + path.parent.mkdir() if project.concept_db is not None: # CDB / vocab based cat.cdb.save(project.concept_db.cdb_file.path, overwrite=True) else: - cat.save_model_pack(project.model_pack.path, - add_hash_to_pack_name=True) + # NOTE: cannot overwrite, so working around + with TemporaryDirectory() as tmp_dir: + # making new folder name so that it's copied + # to the specific path rather than into the folder + temp_folder = os.path.join(tmp_dir, "model_copy") + shutil.move(project.model_pack.path, temp_folder) + try: + cat.save_model_pack( + os.path.dirname(project.model_pack.path), + pack_name=os.path.basename(project.model_pack.path), + add_hash_to_pack_name=False) + except Exception as e: + logger.warning("Unable to save model pack. Restoring previous state") + if os.path.exists(project.model_pack.path): + shutil.rmtree(project.model_pack.path) # remove partial/corrupt output + # restore original + try: + shutil.move(temp_folder, project.model_pack.path) + except Exception as restore_err: + logger.error("Failed to restore model pack:", exc_info=restore_err) + raise return Response({'message': 'Models saved'}) From 6a2d46d4bf66d412a82d4cbb9d30e3dacf7968b0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 7 Apr 2026 13:50:04 +0100 Subject: [PATCH 5/5] CU-869ctq789: Move model pack overwrite to a new/separate function --- medcat-trainer/webapp/api/api/views.py | 49 +++++++++++++------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/medcat-trainer/webapp/api/api/views.py b/medcat-trainer/webapp/api/api/views.py index a64a42aeb..ed287da1c 100644 --- a/medcat-trainer/webapp/api/api/views.py +++ b/medcat-trainer/webapp/api/api/views.py @@ -579,39 +579,40 @@ def save_models(request): p_id = request.data['project_id'] project = ProjectAnnotateEntities.objects.get(id=p_id) cat = get_medcat(project=project) - from pathlib import Path - path = Path() - path.parent.mkdir() if project.concept_db is not None: # CDB / vocab based cat.cdb.save(project.concept_db.cdb_file.path, overwrite=True) else: - # NOTE: cannot overwrite, so working around - with TemporaryDirectory() as tmp_dir: - # making new folder name so that it's copied - # to the specific path rather than into the folder - temp_folder = os.path.join(tmp_dir, "model_copy") - shutil.move(project.model_pack.path, temp_folder) - try: - cat.save_model_pack( - os.path.dirname(project.model_pack.path), - pack_name=os.path.basename(project.model_pack.path), - add_hash_to_pack_name=False) - except Exception as e: - logger.warning("Unable to save model pack. Restoring previous state") - if os.path.exists(project.model_pack.path): - shutil.rmtree(project.model_pack.path) # remove partial/corrupt output - # restore original - try: - shutil.move(temp_folder, project.model_pack.path) - except Exception as restore_err: - logger.error("Failed to restore model pack:", exc_info=restore_err) - raise + _overwrite_model_pack(cat, project.model_pack.path) return Response({'message': 'Models saved'}) +def _overwrite_model_pack(cat, model_path: str): + # NOTE: cannot overwrite, so working around + with TemporaryDirectory() as tmp_dir: + # making new folder name so that it's copied + # to the specific path rather than into the folder + temp_folder = os.path.join(tmp_dir, "model_copy") + shutil.move(model_path, temp_folder) + try: + cat.save_model_pack( + os.path.dirname(model_path), + pack_name=os.path.basename(model_path), + add_hash_to_pack_name=False) + except Exception as e: + logger.warning("Unable to save model pack. Restoring previous state") + if os.path.exists(model_path): + shutil.rmtree(model_path) # remove partial/corrupt output + # restore original + try: + shutil.move(temp_folder, model_path) + except Exception as restore_err: + logger.error("Failed to restore model pack:", exc_info=restore_err) + raise + + @api_view(http_method_names=['POST']) def get_create_entity(request): label = request.data['label']