endoreg-db 0.8.3.7__py3-none-any.whl → 0.8.6.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.
Files changed (41) hide show
  1. endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +23 -1
  2. endoreg_db/data/setup_config.yaml +38 -0
  3. endoreg_db/management/commands/create_model_meta_from_huggingface.py +19 -5
  4. endoreg_db/management/commands/load_ai_model_data.py +18 -15
  5. endoreg_db/management/commands/setup_endoreg_db.py +218 -33
  6. endoreg_db/models/media/pdf/raw_pdf.py +241 -97
  7. endoreg_db/models/media/video/pipe_1.py +30 -33
  8. endoreg_db/models/media/video/video_file.py +300 -187
  9. endoreg_db/models/medical/hardware/endoscopy_processor.py +10 -1
  10. endoreg_db/models/metadata/model_meta_logic.py +63 -43
  11. endoreg_db/models/metadata/sensitive_meta_logic.py +251 -25
  12. endoreg_db/serializers/__init__.py +26 -55
  13. endoreg_db/serializers/misc/__init__.py +1 -1
  14. endoreg_db/serializers/misc/file_overview.py +65 -35
  15. endoreg_db/serializers/misc/{vop_patient_data.py → sensitive_patient_data.py} +1 -1
  16. endoreg_db/serializers/video_examination.py +198 -0
  17. endoreg_db/services/lookup_service.py +228 -58
  18. endoreg_db/services/lookup_store.py +174 -30
  19. endoreg_db/services/pdf_import.py +585 -282
  20. endoreg_db/services/video_import.py +485 -242
  21. endoreg_db/urls/__init__.py +36 -23
  22. endoreg_db/urls/label_video_segments.py +2 -0
  23. endoreg_db/urls/media.py +3 -2
  24. endoreg_db/utils/setup_config.py +177 -0
  25. endoreg_db/views/__init__.py +5 -3
  26. endoreg_db/views/media/pdf_media.py +3 -1
  27. endoreg_db/views/media/video_media.py +1 -1
  28. endoreg_db/views/media/video_segments.py +187 -259
  29. endoreg_db/views/pdf/__init__.py +5 -8
  30. endoreg_db/views/pdf/pdf_stream.py +187 -0
  31. endoreg_db/views/pdf/reimport.py +110 -94
  32. endoreg_db/views/requirement/lookup.py +171 -287
  33. endoreg_db/views/video/__init__.py +0 -2
  34. endoreg_db/views/video/video_examination_viewset.py +202 -289
  35. {endoreg_db-0.8.3.7.dist-info → endoreg_db-0.8.6.3.dist-info}/METADATA +1 -2
  36. {endoreg_db-0.8.3.7.dist-info → endoreg_db-0.8.6.3.dist-info}/RECORD +38 -37
  37. endoreg_db/views/pdf/pdf_media.py +0 -239
  38. endoreg_db/views/pdf/pdf_stream_views.py +0 -127
  39. endoreg_db/views/video/video_media.py +0 -158
  40. {endoreg_db-0.8.3.7.dist-info → endoreg_db-0.8.6.3.dist-info}/WHEEL +0 -0
  41. {endoreg_db-0.8.3.7.dist-info → endoreg_db-0.8.6.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,36 +1,39 @@
1
- from django.urls import path, include
2
1
  from django.conf import settings
3
2
  from django.conf.urls.static import static
3
+ from django.urls import include, path
4
4
  from rest_framework.routers import DefaultRouter
5
5
 
6
- # Phase 1.2: Media Management URLs ✅ IMPLEMENTED
7
- from .media import urlpatterns as media_url_patterns
8
-
9
6
  from endoreg_db.views import (
10
- VideoViewSet,
11
7
  ExaminationViewSet,
12
- VideoExaminationViewSet,
8
+ FindingClassificationViewSet,
13
9
  FindingViewSet,
14
- FindingClassificationViewSet,
10
+ PatientExaminationViewSet,
15
11
  PatientFindingViewSet,
16
- PatientExaminationViewSet
12
+ VideoExaminationViewSet,
13
+ VideoViewSet,
17
14
  )
18
15
 
19
16
  from .anonymization import url_patterns as anonymization_url_patterns
20
- from .classification import url_patterns as classification_url_patterns
21
17
  from .auth import urlpatterns as auth_url_patterns
18
+ from .classification import url_patterns as classification_url_patterns
22
19
  from .examination import urlpatterns as examination_url_patterns
23
20
  from .files import urlpatterns as files_url_patterns
21
+ from .label_video_segment_validate import (
22
+ url_patterns as label_video_segment_validate_url_patterns,
23
+ )
24
24
  from .label_video_segments import url_patterns as label_video_segments_url_patterns
25
- from .label_video_segment_validate import url_patterns as label_video_segment_validate_url_patterns
25
+
26
+ # Phase 1.2: Media Management URLs ✅ IMPLEMENTED
27
+ from .media import urlpatterns as media_url_patterns
28
+ from .patient import urlpatterns as patient_url_patterns
29
+
26
30
  # TODO Phase 1.2: Implement VideoMediaView and PDFMediaView before enabling
27
31
  # from .media import urlpatterns as media_url_patterns
28
32
  from .report import url_patterns as report_url_patterns
29
- from .upload import urlpatterns as upload_url_patterns
30
- from .video import url_patterns as video_url_patterns
31
33
  from .requirements import urlpatterns as requirements_url_patterns
32
- from .patient import urlpatterns as patient_url_patterns
33
34
  from .stats import url_patterns as stats_url_patterns
35
+ from .upload import urlpatterns as upload_url_patterns
36
+ from .video import url_patterns as video_url_patterns
34
37
 
35
38
  api_urls = []
36
39
  api_urls += classification_url_patterns
@@ -50,21 +53,31 @@ api_urls += patient_url_patterns
50
53
  api_urls += stats_url_patterns
51
54
 
52
55
  router = DefaultRouter()
53
- router.register(r'videos', VideoViewSet, basename='videos')
54
- router.register(r'examinations', ExaminationViewSet)
55
- router.register(r'video-examinations', VideoExaminationViewSet, basename='video-examinations')
56
- router.register(r'findings', FindingViewSet)
57
- router.register(r'classifications', FindingClassificationViewSet)
58
- router.register(r'patient-findings', PatientFindingViewSet)
59
- router.register(r'patient-examinations', PatientExaminationViewSet)
56
+ router.register(r"videos", VideoViewSet, basename="videos")
57
+ router.register(r"examinations", ExaminationViewSet)
58
+ router.register(
59
+ r"video-examinations", VideoExaminationViewSet, basename="video-examinations"
60
+ )
61
+ router.register(r"findings", FindingViewSet)
62
+ router.register(r"classifications", FindingClassificationViewSet)
63
+ router.register(r"patient-findings", PatientFindingViewSet)
64
+ router.register(r"patient-examinations", PatientExaminationViewSet)
65
+
66
+ # Additional custom video examination routes
67
+ # Frontend expects: GET /api/video/{id}/examinations/
68
+ video_examinations_list = VideoExaminationViewSet.as_view({"get": "by_video"})
60
69
 
61
70
  # Export raw API urlpatterns (no prefix). The project-level endoreg_db/urls.py mounts these under /api/.
62
71
  urlpatterns = [
63
- path('', include(api_urls)), # Specific routes first
64
- path('', include(router.urls)), # Generic router routes second
72
+ path(
73
+ "video/<int:video_id>/examinations/",
74
+ video_examinations_list,
75
+ name="video-examinations-by-video",
76
+ ),
77
+ path("", include(api_urls)), # Specific routes first
78
+ path("", include(router.urls)), # Generic router routes second
65
79
  ]
66
80
 
67
81
  if settings.DEBUG:
68
82
  urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
69
83
  urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
70
-
@@ -8,6 +8,8 @@ from endoreg_db.views import (
8
8
  get_lvs_by_name_and_video_id
9
9
  )
10
10
 
11
+
12
+
11
13
  url_patterns = [
12
14
  path(
13
15
  "lvs/by-label-name/<str:label_name>/by-video-id/<int:video_id>/",
endoreg_db/urls/media.py CHANGED
@@ -1,3 +1,4 @@
1
+ from PIL.PdfParser import PdfStream
1
2
  from django.urls import path
2
3
 
3
4
  from endoreg_db.views.media import (
@@ -22,6 +23,7 @@ from endoreg_db.views import (
22
23
  VideoStreamView,
23
24
  )
24
25
  from endoreg_db.views.pdf.reimport import PdfReimportView
26
+ from endoreg_db.views.pdf.pdf_stream import PdfStreamView
25
27
  from endoreg_db.views.video.reimport import VideoReimportView
26
28
  from endoreg_db.views.video.correction import (
27
29
  VideoMetadataView,
@@ -216,8 +218,7 @@ urlpatterns = [
216
218
  # PDF media endpoints
217
219
  path("media/pdfs/", PdfMediaView.as_view(), name="pdf-list"),
218
220
  path("media/pdfs/<int:pk>/", PdfMediaView.as_view(), name="pdf-detail"),
219
- path("media/pdfs/<int:pk>/stream/", PdfMediaView.as_view(), name="pdf-stream"),
220
-
221
+ path("media/pdfs/<int:pk>/stream/", PdfStreamView.as_view(), name="pdf-stream"), # Support ?type=raw|anonymized params
221
222
  # PDF Re-import API endpoint (modern media framework)
222
223
  # POST /api/media/pdfs/<int:pk>/reimport/
223
224
  # Re-imports a PDF file to regenerate metadata when OCR failed or data is incomplete
@@ -0,0 +1,177 @@
1
+ """
2
+ Configuration loader for EndoReg DB setup.
3
+ Handles loading and parsing of setup configuration from YAML files.
4
+ """
5
+
6
+ import glob
7
+ import logging
8
+ import os
9
+ from pathlib import Path
10
+ from typing import Any, Dict, List, Optional
11
+
12
+ import yaml
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class SetupConfig:
18
+ """
19
+ Handles loading and accessing setup configuration from YAML files.
20
+ Provides methods to get model names, search patterns, and fallback configurations.
21
+ """
22
+
23
+ def __init__(self, config_file: Optional[Path] = None):
24
+ """
25
+ Initialize the setup configuration.
26
+
27
+ Args:
28
+ config_file: Path to the setup configuration YAML file.
29
+ If None, uses default location.
30
+ """
31
+ if config_file is None:
32
+ # Default to setup_config.yaml in data directory
33
+ config_file = Path(__file__).parent.parent / "data" / "setup_config.yaml"
34
+
35
+ self.config_file = config_file
36
+ self._config = self._load_config()
37
+
38
+ def _load_config(self) -> Dict[str, Any]:
39
+ """Load configuration from YAML file."""
40
+ try:
41
+ if self.config_file.exists():
42
+ with open(self.config_file, "r") as f:
43
+ config = yaml.safe_load(f)
44
+ logger.info(f"Loaded setup configuration from {self.config_file}")
45
+ return config or {}
46
+ else:
47
+ logger.warning(f"Setup config file not found: {self.config_file}")
48
+ return self._get_default_config()
49
+ except Exception as e:
50
+ logger.error(f"Error loading setup config: {e}")
51
+ return self._get_default_config()
52
+
53
+ def _get_default_config(self) -> Dict[str, Any]:
54
+ """Return default configuration if file is not available."""
55
+ return {
56
+ "default_models": {
57
+ "primary_classification_model": "image_multilabel_classification_colonoscopy_default",
58
+ "primary_labelset": "multilabel_classification_colonoscopy_default",
59
+ },
60
+ "huggingface_fallback": {
61
+ "enabled": True,
62
+ "repo_id": "wg-lux/colo_segmentation_RegNetX800MF_base",
63
+ "filename": "colo_segmentation_RegNetX800MF_base.ckpt",
64
+ "labelset_name": "multilabel_classification_colonoscopy_default",
65
+ },
66
+ "weights_search_patterns": [
67
+ "colo_segmentation_RegNetX800MF_*.ckpt",
68
+ "image_multilabel_classification_colonoscopy_default_*.ckpt",
69
+ "*_colonoscopy_*.ckpt",
70
+ ],
71
+ "weights_search_dirs": ["tests/assets", "assets", "data/storage/model_weights", "${STORAGE_DIR}/model_weights"],
72
+ "auto_generation_defaults": {
73
+ "activation": "sigmoid",
74
+ "mean": "0.485,0.456,0.406",
75
+ "std": "0.229,0.224,0.225",
76
+ "size_x": 224,
77
+ "size_y": 224,
78
+ "axes": "CHW",
79
+ "batchsize": 32,
80
+ "num_workers": 4,
81
+ },
82
+ }
83
+
84
+ def get_primary_model_name(self) -> str:
85
+ """Get the primary classification model name."""
86
+ return self._config.get("default_models", {}).get("primary_classification_model", "image_multilabel_classification_colonoscopy_default")
87
+
88
+ def get_primary_labelset_name(self) -> str:
89
+ """Get the primary labelset name."""
90
+ return self._config.get("default_models", {}).get("primary_labelset", "multilabel_classification_colonoscopy_default")
91
+
92
+ def get_huggingface_config(self) -> Dict[str, Any]:
93
+ """Get HuggingFace fallback configuration."""
94
+ return self._config.get("huggingface_fallback", {})
95
+
96
+ def get_weights_search_patterns(self) -> List[str]:
97
+ """Get weight file search patterns."""
98
+ return self._config.get("weights_search_patterns", ["colo_segmentation_RegNetX800MF_*.ckpt", "*_colonoscopy_*.ckpt"])
99
+
100
+ def get_weights_search_dirs(self) -> List[Path]:
101
+ """
102
+ Get weight file search directories with environment variable substitution.
103
+ """
104
+ dirs = self._config.get("weights_search_dirs", [])
105
+ resolved_dirs = []
106
+
107
+ for dir_str in dirs:
108
+ # Handle environment variable substitution
109
+ if "${" in dir_str:
110
+ dir_str = os.path.expandvars(dir_str)
111
+
112
+ resolved_dirs.append(Path(dir_str))
113
+
114
+ return resolved_dirs
115
+
116
+ def get_auto_generation_defaults(self) -> Dict[str, Any]:
117
+ """Get default values for auto-generated metadata."""
118
+ return self._config.get("auto_generation_defaults", {})
119
+
120
+ def find_model_weights_files(self) -> List[Path]:
121
+ """
122
+ Find model weight files using configured search patterns and directories.
123
+
124
+ Returns:
125
+ List of paths to found weight files
126
+ """
127
+ found_files = []
128
+ search_dirs = self.get_weights_search_dirs()
129
+ search_patterns = self.get_weights_search_patterns()
130
+
131
+ for search_dir in search_dirs:
132
+ if not search_dir.exists():
133
+ continue
134
+
135
+ for pattern in search_patterns:
136
+ # Use glob to find files matching pattern
137
+ pattern_path = search_dir / pattern
138
+ matches = glob.glob(str(pattern_path))
139
+ for match in matches:
140
+ path = Path(match)
141
+ if path.exists() and path not in found_files:
142
+ found_files.append(path)
143
+ logger.info(f"Found weight file: {path}")
144
+
145
+ return found_files
146
+
147
+ def get_model_specific_config(self, model_name: str) -> Optional[Dict[str, Any]]:
148
+ """
149
+ Get model-specific configuration from YAML metadata files.
150
+
151
+ Args:
152
+ model_name: Name of the model to get config for
153
+
154
+ Returns:
155
+ Model-specific setup configuration if found
156
+ """
157
+ # This would need to parse the ai_model_meta YAML files
158
+ # and extract setup_config sections for the specified model
159
+ try:
160
+ from endoreg_db.data import AI_MODEL_META_DATA_DIR
161
+
162
+ for yaml_file in AI_MODEL_META_DATA_DIR.glob("*.yaml"):
163
+ with open(yaml_file, "r") as f:
164
+ data = yaml.safe_load(f)
165
+
166
+ if isinstance(data, list):
167
+ for item in data:
168
+ if item.get("fields", {}).get("name") == model_name or item.get("fields", {}).get("model") == model_name:
169
+ return item.get("setup_config", {})
170
+ except Exception as e:
171
+ logger.warning(f"Error loading model-specific config for {model_name}: {e}")
172
+
173
+ return None
174
+
175
+
176
+ # Global instance for easy access
177
+ setup_config = SetupConfig()
@@ -108,8 +108,8 @@ from .patient_finding_classification import (
108
108
  )
109
109
 
110
110
  from .pdf import (
111
- ClosingFileWrapper,
112
- PDFMediaView,
111
+ PdfReimportView,
112
+ PdfStreamView,
113
113
  )
114
114
 
115
115
  from .report import (
@@ -240,7 +240,9 @@ __all__ = [
240
240
  "create_patient_finding_classification",
241
241
 
242
242
  # PDF
243
- "ClosingFileWrapper",
243
+ "PdfMediaView",
244
+ "PdfReimportView",
245
+ "PdfStreamView",
244
246
 
245
247
  # Report
246
248
  "ReportListView",
@@ -13,6 +13,7 @@ from django.http import Http404, FileResponse
13
13
  from rest_framework import status
14
14
  from rest_framework.response import Response
15
15
  from rest_framework.views import APIView
16
+ from django.views.decorators.clickjacking import xframe_options_exempt
16
17
  from django.db.models import Q
17
18
 
18
19
  from endoreg_db.models import RawPdfFile
@@ -133,7 +134,8 @@ class PdfMediaView(APIView):
133
134
  {"error": "Failed to retrieve PDF details"},
134
135
  status=status.HTTP_500_INTERNAL_SERVER_ERROR
135
136
  )
136
-
137
+
138
+ @xframe_options_exempt
137
139
  def _stream_pdf(self, pk):
138
140
  """
139
141
  Stream PDF file content for viewing/download.
@@ -30,7 +30,7 @@ class VideoMediaView(APIView):
30
30
  - GET /api/media/videos/ - List all videos with filtering
31
31
  - GET /api/media/videos/{id}/ - Get video details
32
32
  - PATCH /api/media/videos/{id}/ - Update video metadata (future)
33
- - DELETE /api/media/videos/{id}/ - Delete video (future)
33
+ - DELETE /api/media/videos/{id}/ - Delete video
34
34
 
35
35
  Query Parameters:
36
36
  - status: Filter by processing status (not_started, processing, done, failed, validated)