endoreg-db 0.8.3.3__py3-none-any.whl → 0.8.6.5__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.

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 +1 -2
  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 +34 -45
  11. endoreg_db/models/metadata/sensitive_meta_logic.py +555 -150
  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 +493 -240
  21. endoreg_db/urls/__init__.py +36 -23
  22. endoreg_db/urls/label_video_segments.py +2 -0
  23. endoreg_db/urls/media.py +103 -66
  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 +186 -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.3.dist-info → endoreg_db-0.8.6.5.dist-info}/METADATA +1 -2
  36. {endoreg_db-0.8.3.3.dist-info → endoreg_db-0.8.6.5.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.3.dist-info → endoreg_db-0.8.6.5.dist-info}/WHEEL +0 -0
  41. {endoreg_db-0.8.3.3.dist-info → endoreg_db-0.8.6.5.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,35 +1,36 @@
1
1
  from django.urls import path
2
+ from PIL.PdfParser import PdfStream
2
3
 
4
+ from endoreg_db.views import VideoStreamView
3
5
  from endoreg_db.views.media import (
4
- VideoMediaView,
5
6
  PdfMediaView, # Alias to avoid conflict with legacy pdf.PDFMediaView
7
+ VideoMediaView,
8
+ pdf_sensitive_metadata,
9
+ pdf_sensitive_metadata_list,
10
+ pdf_sensitive_metadata_verify,
11
+ sensitive_metadata_list,
12
+ video_segment_detail,
13
+ video_segment_validate,
6
14
  video_segments_by_pk,
7
- video_segments_collection,
8
15
  video_segments_by_video,
9
- video_segment_detail,
16
+ video_segments_collection,
10
17
  video_segments_stats,
11
- video_segment_validate,
12
18
  video_segments_validate_bulk,
13
19
  video_segments_validation_status,
14
20
  video_sensitive_metadata,
15
21
  video_sensitive_metadata_verify,
16
- pdf_sensitive_metadata,
17
- pdf_sensitive_metadata_verify,
18
- sensitive_metadata_list,
19
- pdf_sensitive_metadata_list,
20
- )
21
- from endoreg_db.views import (
22
- VideoStreamView,
23
22
  )
23
+ from endoreg_db.views.pdf.pdf_stream import PdfStreamView
24
24
  from endoreg_db.views.pdf.reimport import PdfReimportView
25
- from endoreg_db.views.video.reimport import VideoReimportView
26
25
  from endoreg_db.views.video.correction import (
26
+ VideoApplyMaskView,
27
+ VideoCorrectionView,
27
28
  VideoMetadataView,
28
29
  VideoProcessingHistoryView,
29
- VideoApplyMaskView,
30
30
  VideoRemoveFramesView,
31
- VideoCorrectionView,
32
31
  )
32
+ from endoreg_db.views.video.reimport import VideoReimportView
33
+
33
34
  # ---------------------------------------------------------------------------------------
34
35
  # ANNOTATION API ENDPOINTS
35
36
  #
@@ -39,19 +40,27 @@ from endoreg_db.views.video.correction import (
39
40
  # ---------------------------------------------------------------------------------------
40
41
 
41
42
  # Simplified Meta and Validation Endpoints
42
-
43
+
43
44
  urlpatterns = [
44
45
  # Video media endpoints
45
46
  path("media/videos/", VideoMediaView.as_view(), name="video-list"),
46
- path("media/videos/<int:pk>/", VideoStreamView.as_view(), name="video-detail-stream"), # Support ?type= params
47
- path("media/videos/<int:pk>/details/", VideoMediaView.as_view(), name="video-detail"), # JSON metadata
48
- path("media/videos/<int:pk>/stream/", VideoStreamView.as_view(), name="video-stream"), # Legacy support
49
-
47
+ path(
48
+ "media/videos/<int:pk>/", VideoStreamView.as_view(), name="video-detail-stream"
49
+ ), # Support ?type= params
50
+ path(
51
+ "media/videos/<int:pk>/details/", VideoMediaView.as_view(), name="video-detail"
52
+ ), # JSON metadata
53
+ path(
54
+ "media/videos/<int:pk>/stream/", VideoStreamView.as_view(), name="video-stream"
55
+ ), # Legacy support
50
56
  # Video Re-import API endpoint (modern media framework)
51
57
  # POST /api/media/videos/<int:pk>/reimport/
52
58
  # Re-imports a video file to regenerate metadata when OCR failed or data is incomplete
53
- path("media/videos/<int:pk>/reimport/", VideoReimportView.as_view(), name="video-reimport"),
54
-
59
+ path(
60
+ "media/videos/<int:pk>/reimport/",
61
+ VideoReimportView.as_view(),
62
+ name="video-reimport",
63
+ ),
55
64
  # ---------------------------------------------------------------------------------------
56
65
  # VIDEO CORRECTION API ENDPOINTS (Modern Media Framework - October 14, 2025)
57
66
  #
@@ -64,38 +73,51 @@ urlpatterns = [
64
73
  # - Metadata: View analysis results
65
74
  # - History: Track all correction operations
66
75
  # ---------------------------------------------------------------------------------------
67
-
68
76
  # Video Correction API
69
77
  # GET /api/media/videos/video-correction/{id}/ - Get video details for correction
70
- path("media/videos/video-correction/<int:pk>", VideoCorrectionView.as_view(), name="video-correction"),
71
-
78
+ path(
79
+ "media/videos/video-correction/<int:pk>",
80
+ VideoCorrectionView.as_view(),
81
+ name="video-correction",
82
+ ),
72
83
  # Video Metadata API
73
84
  # GET /api/media/videos/<int:pk>/metadata/
74
85
  # Returns analysis results (sensitive frame count, ratio, frame IDs)
75
- path("media/videos/<int:pk>/metadata/", VideoMetadataView.as_view(), name="video-metadata"),
76
-
86
+ path(
87
+ "media/videos/<int:pk>/metadata/",
88
+ VideoMetadataView.as_view(),
89
+ name="video-metadata",
90
+ ),
77
91
  # Video Processing History API
78
92
  # GET /api/media/videos/<int:pk>/processing-history/
79
93
  # Returns history of all processing operations (masking, frame removal, analysis)
80
- path("media/videos/<int:pk>/processing-history/", VideoProcessingHistoryView.as_view(), name="video-processing-history"),
81
-
94
+ path(
95
+ "media/videos/<int:pk>/processing-history/",
96
+ VideoProcessingHistoryView.as_view(),
97
+ name="video-processing-history",
98
+ ),
82
99
  # Video Analysis API
83
100
  # POST /api/media/videos/<int:pk>/analyze/
84
101
  # Analyzes video for sensitive frames using MiniCPM-o 2.6 or OCR+LLM
85
102
  # Body: { detection_method: 'minicpm'|'ocr_llm'|'hybrid', sample_interval: 30 }
86
-
87
103
  # Video Masking API
88
104
  # POST /api/media/videos/<int:pk>/apply-mask/
89
105
  # Applies device mask or custom ROI mask to video
90
106
  # Body: { mask_type: 'device'|'custom', device_name: 'olympus', roi: {...} }
91
- path("media/videos/<int:pk>/apply-mask/", VideoApplyMaskView.as_view(), name="video-apply-mask"),
92
-
107
+ path(
108
+ "media/videos/<int:pk>/apply-mask/",
109
+ VideoApplyMaskView.as_view(),
110
+ name="video-apply-mask",
111
+ ),
93
112
  # Video Frame Removal API
94
113
  # POST /api/media/videos/<int:pk>/remove-frames/
95
114
  # Removes specified frames from video
96
115
  # Body: { frame_list: [10,20,30] OR frame_ranges: '10-20,30' OR detection_method: 'automatic' }
97
- path("media/videos/<int:pk>/remove-frames/", VideoRemoveFramesView.as_view(), name="video-remove-frames"),
98
-
116
+ path(
117
+ "media/videos/<int:pk>/remove-frames/",
118
+ VideoRemoveFramesView.as_view(),
119
+ name="video-remove-frames",
120
+ ),
99
121
  # ---------------------------------------------------------------------------------------
100
122
  # VIDEO SEGMENT API ENDPOINTS (Modern Media Framework - October 14, 2025)
101
123
  #
@@ -104,29 +126,40 @@ urlpatterns = [
104
126
  # Video-scoped: GET/POST segments for specific video
105
127
  # Detail: GET/PATCH/DELETE individual segment
106
128
  # ---------------------------------------------------------------------------------------
107
-
108
129
  # Video Segments Collection API
109
130
  # GET/POST /api/media/videos/segments/
110
131
  # List all video segments across videos or create new segment
111
- path("media/videos/segments/", video_segments_collection, name="video-segments-collection"),
112
-
132
+ path(
133
+ "media/videos/segments/",
134
+ video_segments_collection,
135
+ name="video-segments-collection",
136
+ ),
113
137
  # Video Segments Stats API
114
138
  # GET /api/media/videos/segments/stats/
115
139
  # Get statistics about video segments
116
- path("media/videos/segments/stats/", video_segments_stats, name="video-segments-stats"),
117
-
140
+ path(
141
+ "media/videos/segments/stats/",
142
+ video_segments_stats,
143
+ name="video-segments-stats",
144
+ ),
118
145
  # Video-Specific Segments API
119
146
  # GET/POST /api/media/videos/<int:pk>/segments/
120
147
  # List segments for specific video or create segment for video
121
- path("media/videos/<int:pk>/segments/", video_segments_by_video, name="video-segments-by-video"),
122
-
148
+ path(
149
+ "media/videos/<int:pk>/segments/",
150
+ video_segments_by_video,
151
+ name="video-segments-by-video",
152
+ ),
123
153
  # Segment Detail API
124
154
  # GET /api/media/videos/<int:pk>/segments/<int:segment_id>/
125
155
  # PATCH /api/media/videos/<int:pk>/segments/<int:segment_id>/
126
156
  # DELETE /api/media/videos/<int:pk>/segments/<int:segment_id>/
127
157
  # Manages individual segment operations
128
- path("media/videos/<int:pk>/segments/<int:segment_id>/", video_segment_detail, name="video-segment-detail"),
129
-
158
+ path(
159
+ "media/videos/<int:pk>/segments/<int:segment_id>/",
160
+ video_segment_detail,
161
+ name="video-segment-detail",
162
+ ),
130
163
  # ---------------------------------------------------------------------------------------
131
164
  # VIDEO SEGMENT VALIDATION API ENDPOINTS (Modern Media Framework - October 14, 2025)
132
165
  #
@@ -135,65 +168,68 @@ urlpatterns = [
135
168
  # Bulk: POST validate multiple segments
136
169
  # Status: GET/POST validation status for all segments
137
170
  # ---------------------------------------------------------------------------------------
138
-
139
171
  # Single Segment Validation API
140
172
  # POST /api/media/videos/<int:pk>/segments/<int:segment_id>/validate/
141
173
  # Validates a single video segment
142
174
  # Body: { "is_validated": true, "notes": "..." }
143
- path("media/videos/<int:pk>/segments/<int:segment_id>/validate/", video_segment_validate, name="video-segment-validate"),
144
-
175
+ path(
176
+ "media/videos/<int:pk>/segments/<int:segment_id>/validate/",
177
+ video_segment_validate,
178
+ name="video-segment-validate",
179
+ ),
145
180
  # Bulk Segment Validation API
146
181
  # POST /api/media/videos/<int:pk>/segments/validate-bulk/
147
182
  # Validates multiple segments at once
148
183
  # Body: { "segment_ids": [1,2,3], "is_validated": true, "notes": "..." }
149
- path("media/videos/<int:pk>/segments/validate-bulk/", video_segments_validate_bulk, name="video-segments-validate-bulk"),
150
-
184
+ path(
185
+ "media/videos/<int:pk>/segments/validate-bulk/",
186
+ video_segments_validate_bulk,
187
+ name="video-segments-validate-bulk",
188
+ ),
151
189
  # Segment Validation Status API
152
190
  # GET /api/media/videos/<int:pk>/segments/validation-status/
153
191
  # Returns validation statistics for all segments
154
192
  # POST /api/media/videos/<int:pk>/segments/validation-status/
155
193
  # Marks all segments (or filtered by label) as validated
156
194
  # Body: { "label_name": "polyp", "notes": "..." }
157
- path("media/videos/<int:pk>/segments/validation-status/", video_segments_validation_status, name="video-segments-validation-status"),
158
-
195
+ path(
196
+ "media/videos/<int:pk>/segments/validation-status/",
197
+ video_segments_validation_status,
198
+ name="video-segments-validation-status",
199
+ ),
159
200
  # ---------------------------------------------------------------------------------------
160
201
  # SENSITIVE METADATA ENDPOINTS (Modern Media Framework)
161
202
  # ---------------------------------------------------------------------------------------
162
-
163
203
  # Video Sensitive Metadata (Resource-Scoped)
164
204
  # GET/PATCH /api/media/videos/<pk>/sensitive-metadata/
165
205
  # Get or update sensitive patient data for a video
166
206
  path(
167
207
  "media/videos/<int:pk>/sensitive-metadata/",
168
208
  video_sensitive_metadata,
169
- name="video-sensitive-metadata"
209
+ name="video-sensitive-metadata",
170
210
  ),
171
-
172
211
  # POST /api/media/videos/<pk>/sensitive-metadata/verify/
173
212
  # Update verification state (dob_verified, names_verified)
174
213
  path(
175
214
  "media/videos/<int:pk>/sensitive-metadata/verify/",
176
215
  video_sensitive_metadata_verify,
177
- name="video-sensitive-metadata-verify"
216
+ name="video-sensitive-metadata-verify",
178
217
  ),
179
-
180
218
  # PDF Sensitive Metadata (Resource-Scoped)
181
219
  # GET/PATCH /api/media/pdfs/<pk>/sensitive-metadata/
182
220
  # Get or update sensitive patient data for a PDF
183
221
  path(
184
222
  "media/pdfs/<int:pk>/sensitive-metadata/",
185
223
  pdf_sensitive_metadata,
186
- name="pdf-sensitive-metadata"
224
+ name="pdf-sensitive-metadata",
187
225
  ),
188
-
189
226
  # POST /api/media/pdfs/<pk>/sensitive-metadata/verify/
190
227
  # Update verification state (dob_verified, names_verified)
191
228
  path(
192
229
  "media/pdfs/<int:pk>/sensitive-metadata/verify/",
193
230
  pdf_sensitive_metadata_verify,
194
- name="pdf-sensitive-metadata-verify"
231
+ name="pdf-sensitive-metadata-verify",
195
232
  ),
196
-
197
233
  # List Endpoints (Collection-Level)
198
234
  # GET /api/media/sensitive-metadata/
199
235
  # List all sensitive metadata (combined PDFs and Videos)
@@ -201,26 +237,27 @@ urlpatterns = [
201
237
  path(
202
238
  "media/sensitive-metadata/",
203
239
  sensitive_metadata_list,
204
- name="sensitive-metadata-list"
240
+ name="sensitive-metadata-list",
205
241
  ),
206
-
207
242
  # GET /api/media/pdfs/sensitive-metadata/
208
243
  # List sensitive metadata for PDFs only
209
244
  # Replaces legacy /api/pdf/sensitivemeta/list/
210
245
  path(
211
246
  "media/pdfs/sensitive-metadata/",
212
247
  pdf_sensitive_metadata_list,
213
- name="pdf-sensitive-metadata-list"
248
+ name="pdf-sensitive-metadata-list",
214
249
  ),
215
-
216
250
  # PDF media endpoints
217
251
  path("media/pdfs/", PdfMediaView.as_view(), name="pdf-list"),
218
252
  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
-
253
+ path(
254
+ "media/pdfs/<int:pk>/stream/", PdfStreamView.as_view(), name="pdf-stream"
255
+ ), # Support ?type=raw|anonymized params
221
256
  # PDF Re-import API endpoint (modern media framework)
222
257
  # POST /api/media/pdfs/<int:pk>/reimport/
223
258
  # Re-imports a PDF file to regenerate metadata when OCR failed or data is incomplete
224
- path("media/pdfs/<int:pk>/reimport/", PdfReimportView.as_view(), name="pdf-reimport"),
259
+ path(
260
+ "media/pdfs/<int:pk>/reimport/", PdfReimportView.as_view(), name="pdf-reimport"
261
+ ),
225
262
  ]
226
- # ---------------------------------------------------------------------------------------
263
+ # ---------------------------------------------------------------------------------------
@@ -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)