endoreg-db 0.8.4.2__py3-none-any.whl → 0.8.4.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of endoreg-db might be problematic. Click here for more details.

@@ -2,7 +2,7 @@ import shutil
2
2
  from logging import getLogger
3
3
  from pathlib import Path
4
4
  from typing import TYPE_CHECKING, Any, Optional, Type
5
- from django.core.files import File
5
+
6
6
  from django.db import transaction
7
7
  from huggingface_hub import hf_hub_download
8
8
 
@@ -18,18 +18,14 @@ if TYPE_CHECKING:
18
18
  from .model_meta import ModelMeta # Import ModelMeta for type hinting
19
19
 
20
20
 
21
- def get_latest_version_number_logic(
22
- cls: Type["ModelMeta"], meta_name: str, model_name: str
23
- ) -> int:
21
+ def get_latest_version_number_logic(cls: Type["ModelMeta"], meta_name: str, model_name: str) -> int:
24
22
  """
25
23
  Finds the highest numerical version for a given meta_name and model_name.
26
24
  Iterates through all versions, attempts to parse them as integers,
27
25
  and returns the maximum integer found. If no numeric versions are found,
28
26
  returns 0.
29
27
  """
30
- versions_qs = cls.objects.filter(
31
- name=meta_name, model__name=model_name
32
- ).values_list("version", flat=True)
28
+ versions_qs = cls.objects.filter(name=meta_name, model__name=model_name).values_list("version", flat=True)
33
29
 
34
30
  max_v = 0
35
31
  found_numeric_version = False
@@ -84,24 +80,17 @@ def create_from_file_logic(
84
80
 
85
81
  if requested_version:
86
82
  target_version = str(requested_version)
87
- existing = cls.objects.filter(
88
- name=meta_name, model=ai_model, version=target_version
89
- ).first()
83
+ existing = cls.objects.filter(name=meta_name, model=ai_model, version=target_version).first()
90
84
  if existing and not bump_if_exists:
91
85
  raise ValueError(
92
- f"ModelMeta '{meta_name}' version '{target_version}' for model '{model_name}' "
93
- f"already exists. Use bump_if_exists=True to increment."
86
+ f"ModelMeta '{meta_name}' version '{target_version}' for model '{model_name}' already exists. Use bump_if_exists=True to increment."
94
87
  )
95
88
  elif existing and bump_if_exists:
96
89
  target_version = str(latest_version_num + 1)
97
- logger.info(
98
- f"Bumping version for {meta_name}/{model_name} to {target_version}"
99
- )
90
+ logger.info(f"Bumping version for {meta_name}/{model_name} to {target_version}")
100
91
  else:
101
92
  target_version = str(latest_version_num + 1)
102
- logger.info(
103
- f"Setting next version for {meta_name}/{model_name} to {target_version}"
104
- )
93
+ logger.info(f"Setting next version for {meta_name}/{model_name} to {target_version}")
105
94
 
106
95
  # --- Prepare Weights File ---
107
96
  source_weights_path = Path(weights_file).resolve()
@@ -111,10 +100,7 @@ def create_from_file_logic(
111
100
  # Construct destination path within MEDIA_ROOT/WEIGHTS_DIR
112
101
  weights_filename = source_weights_path.name
113
102
  # Relative path for the FileField upload_to
114
- relative_dest_path = (
115
- Path(WEIGHTS_DIR.relative_to(STORAGE_DIR))
116
- / f"{meta_name}_v{target_version}_{weights_filename}"
117
- )
103
+ relative_dest_path = Path(WEIGHTS_DIR.relative_to(STORAGE_DIR)) / f"{meta_name}_v{target_version}_{weights_filename}"
118
104
  # Full path for shutil.copy
119
105
  full_dest_path = STORAGE_DIR / relative_dest_path
120
106
 
@@ -127,8 +113,6 @@ def create_from_file_logic(
127
113
  logger.info(f"Copied weights from {source_weights_path} to {full_dest_path}")
128
114
  except Exception as e:
129
115
  raise IOError(f"Failed to copy weights file: {e}") from e
130
-
131
-
132
116
 
133
117
  # --- Create/Update ModelMeta Instance ---
134
118
  defaults = {
@@ -146,11 +130,6 @@ def create_from_file_logic(
146
130
  version=target_version,
147
131
  defaults=defaults,
148
132
  )
149
-
150
- with open(full_dest_path, "rb") as f:
151
- model_meta.weights.save(relative_dest_path.name, File(f), save=False)
152
- model_meta.save()
153
-
154
133
 
155
134
  if created:
156
135
  logger.info(f"Created new ModelMeta: {model_meta}")
@@ -160,8 +139,8 @@ def create_from_file_logic(
160
139
  # --- Optionally update AiModel's active_meta ---
161
140
  # You might want to add logic here to automatically set the newly created/updated
162
141
  # meta as the active one for the AiModel, e.g.:
163
- # ai_model.active_meta = model_meta
164
- # ai_model.save()
142
+ ai_model.active_meta = model_meta
143
+ ai_model.save()
165
144
 
166
145
  return model_meta
167
146
 
@@ -240,22 +219,14 @@ def get_model_meta_by_name_version_logic(
240
219
  try:
241
220
  return cls.objects.get(name=meta_name, model=ai_model, version=version)
242
221
  except Exception as exc:
243
- raise cls.DoesNotExist(
244
- f"ModelMeta '{meta_name}' version '{version}' for model '{model_name}' not found."
245
- ) from exc
222
+ raise cls.DoesNotExist(f"ModelMeta '{meta_name}' version '{version}' for model '{model_name}' not found.") from exc
246
223
  else:
247
224
  # Get latest version
248
- latest = (
249
- cls.objects.filter(name=meta_name, model=ai_model)
250
- .order_by("-date_created")
251
- .first()
252
- )
225
+ latest = cls.objects.filter(name=meta_name, model=ai_model).order_by("-date_created").first()
253
226
  if latest:
254
227
  return latest
255
228
  else:
256
- raise cls.DoesNotExist(
257
- f"No ModelMeta found for '{meta_name}' and model '{model_name}'."
258
- )
229
+ raise cls.DoesNotExist(f"No ModelMeta found for '{meta_name}' and model '{model_name}'.")
259
230
 
260
231
 
261
232
  import re
@@ -273,9 +244,7 @@ def infer_default_model_meta_from_hf(model_id: str) -> dict[str, Any]:
273
244
  """
274
245
 
275
246
  if not (info := model_info(model_id)):
276
- logger.info(
277
- f"Could not retrieve model info for {model_id}, using ColoReg segmentation defaults."
278
- )
247
+ logger.info(f"Could not retrieve model info for {model_id}, using ColoReg segmentation defaults.")
279
248
  return {
280
249
  "name": "wg-lux/colo_segmentation_RegNetX800MF_base",
281
250
  "activation": "sigmoid",
@@ -324,9 +293,7 @@ def infer_default_model_meta_from_hf(model_id: str) -> dict[str, Any]:
324
293
  }
325
294
 
326
295
 
327
- def setup_default_from_huggingface_logic(
328
- cls, model_id: str, labelset_name: str | None = None
329
- ):
296
+ def setup_default_from_huggingface_logic(cls, model_id: str, labelset_name: str | None = None):
330
297
  """
331
298
  Downloads model weights from Hugging Face and auto-fills ModelMeta fields.
332
299
  """
@@ -340,11 +307,11 @@ def setup_default_from_huggingface_logic(
340
307
  )
341
308
 
342
309
  ai_model, _ = AiModel.objects.get_or_create(name=meta["name"])
343
- labelset = (
344
- LabelSet.objects.first()
345
- if not labelset_name
346
- else LabelSet.objects.get(name=labelset_name)
347
- )
310
+ labelset = LabelSet.objects.first() if not labelset_name else LabelSet.objects.get(name=labelset_name)
311
+ model_meta = ModelMeta.objects.filter(name=meta["name"], model=ai_model).first()
312
+ if model_meta:
313
+ logger.info(f"ModelMeta {meta['name']} for model {ai_model.name} already exists. Skipping creation.")
314
+ return model_meta
348
315
 
349
316
  return create_from_file_logic(
350
317
  cls,
@@ -359,3 +326,99 @@ def setup_default_from_huggingface_logic(
359
326
  size_y=meta["size_y"],
360
327
  description=meta["description"],
361
328
  )
329
+
330
+
331
+ def validate_and_fix_ai_model_metadata_logic():
332
+ """
333
+ Validates that all AI models have proper active metadata and fixes any issues.
334
+ This prevents the "No model metadata found for this model" error.
335
+
336
+ Returns:
337
+ dict: Summary of fixes applied
338
+ """
339
+ from ..administration.ai.ai_model import AiModel
340
+ from ..label.label_set import LabelSet
341
+
342
+ summary = {"models_checked": 0, "models_fixed": 0, "metadata_created": 0, "active_meta_set": 0, "errors": []}
343
+
344
+ try:
345
+ all_models = AiModel.objects.all()
346
+ summary["models_checked"] = all_models.count()
347
+
348
+ for model in all_models:
349
+ logger.info(f"Validating model: {model.name}")
350
+
351
+ # Check if model has metadata versions
352
+ metadata_count = model.metadata_versions.count()
353
+
354
+ if metadata_count == 0:
355
+ # Create metadata for models that don't have any
356
+ logger.info(f"Creating metadata for {model.name}")
357
+
358
+ # Use existing labelset or create default
359
+ labelset = LabelSet.objects.first()
360
+ if not labelset:
361
+ labelset = LabelSet.objects.create(name="default_colonoscopy_labels", description="Default colonoscopy classification labels")
362
+
363
+ # Import here to avoid circular imports
364
+ from .model_meta import ModelMeta
365
+
366
+ # Create basic metadata
367
+ meta = ModelMeta.objects.create(
368
+ name=model.name,
369
+ version="1.0",
370
+ model=model,
371
+ labelset=labelset,
372
+ activation="sigmoid" if "classification" in model.name else "sigmoid",
373
+ mean="0.485,0.456,0.406", # ImageNet defaults
374
+ std="0.229,0.224,0.225", # ImageNet defaults
375
+ size_x=224,
376
+ size_y=224,
377
+ axes="CHW",
378
+ batchsize=32,
379
+ num_workers=4,
380
+ description=f"Auto-generated metadata for {model.name}",
381
+ )
382
+
383
+ model.active_meta = meta
384
+ model.save()
385
+ summary["models_fixed"] += 1
386
+ summary["metadata_created"] += 1
387
+ logger.info(f"Created and set metadata for {model.name}")
388
+
389
+ elif not model.active_meta:
390
+ # Model has metadata but no active meta set
391
+ first_meta = model.metadata_versions.first()
392
+ if first_meta:
393
+ logger.info(f"Setting active metadata for {model.name}")
394
+ model.active_meta = first_meta
395
+ model.save()
396
+ summary["models_fixed"] += 1
397
+ summary["active_meta_set"] += 1
398
+ logger.info(f"Set active metadata: {first_meta.name} v{first_meta.version}")
399
+ else:
400
+ error_msg = f"No metadata versions available for {model.name}"
401
+ logger.warning(error_msg)
402
+ summary["errors"].append(error_msg)
403
+
404
+ else:
405
+ logger.info(f"Model {model.name} has valid active metadata: {model.active_meta}")
406
+
407
+ # Verify all models can get latest version
408
+ logger.info("Testing model metadata access...")
409
+ for model in all_models:
410
+ try:
411
+ latest = model.get_latest_version()
412
+ logger.info(f"✅ {model.name}: {latest}")
413
+ except Exception as e:
414
+ error_msg = f"Model {model.name} metadata test failed: {e}"
415
+ logger.error(error_msg)
416
+ summary["errors"].append(error_msg)
417
+
418
+ return summary
419
+
420
+ except Exception as e:
421
+ error_msg = f"Validation failed: {e}"
422
+ logger.error(error_msg)
423
+ summary["errors"].append(error_msg)
424
+ return summary