endoreg-db 0.6.2__py3-none-any.whl → 0.6.4__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 (97) hide show
  1. endoreg_db/data/__init__.py +14 -0
  2. endoreg_db/data/disease_classification/chronic_kidney_disease.yaml +2 -2
  3. endoreg_db/data/disease_classification_choice/chronic_kidney_disease.yaml +6 -6
  4. endoreg_db/data/distribution/numeric/data.yaml +1 -1
  5. endoreg_db/data/examination/examinations/data.yaml +22 -21
  6. endoreg_db/data/examination/type/data.yaml +12 -0
  7. endoreg_db/data/examination_indication/endoscopy.yaml +417 -1
  8. endoreg_db/data/examination_indication_classification/endoscopy.yaml +157 -5
  9. endoreg_db/data/finding/data.yaml +18 -11
  10. endoreg_db/data/finding_intervention/endoscopy.yaml +26 -121
  11. endoreg_db/data/finding_intervention/endoscopy_colonoscopy.yaml +163 -0
  12. endoreg_db/data/finding_intervention/endoscopy_egd.yaml +128 -0
  13. endoreg_db/data/finding_intervention/endoscopy_ercp.yaml +32 -0
  14. endoreg_db/data/finding_intervention/endoscopy_eus_lower.yaml +9 -0
  15. endoreg_db/data/finding_intervention/endoscopy_eus_upper.yaml +36 -0
  16. endoreg_db/data/information_source/endoscopy_guidelines.yaml +7 -0
  17. endoreg_db/data/medication_indication/anticoagulation.yaml +4 -4
  18. endoreg_db/data/pdf_type/data.yaml +9 -16
  19. endoreg_db/data/requirement/colonoscopy_indications.yaml +56 -0
  20. endoreg_db/data/requirement/disease_cardiovascular.yaml +79 -0
  21. endoreg_db/data/requirement/disease_classification_choice_cardiovascular.yaml +38 -0
  22. endoreg_db/data/requirement/disease_hepatology.yaml +12 -0
  23. endoreg_db/data/requirement/disease_misc.yaml +12 -0
  24. endoreg_db/data/requirement/disease_renal.yaml +80 -0
  25. endoreg_db/data/requirement/event_cardiology.yaml +251 -0
  26. endoreg_db/data/requirement/lab_value.yaml +120 -0
  27. endoreg_db/data/requirement_operator/lab_operators.yaml +128 -0
  28. endoreg_db/data/requirement_operator/model_operators.yaml +90 -0
  29. endoreg_db/data/requirement_set/endoscopy_bleeding_risk.yaml +12 -0
  30. endoreg_db/data/requirement_set_type/data.yaml +20 -0
  31. endoreg_db/data/requirement_type/requirement_types.yaml +83 -0
  32. endoreg_db/data/risk/bleeding.yaml +26 -0
  33. endoreg_db/data/risk/thrombosis.yaml +37 -0
  34. endoreg_db/data/risk_type/data.yaml +27 -0
  35. endoreg_db/data/unit/time.yaml +36 -1
  36. endoreg_db/management/commands/load_base_db_data.py +14 -1
  37. endoreg_db/management/commands/load_center_data.py +46 -21
  38. endoreg_db/management/commands/load_examination_indication_data.py +49 -27
  39. endoreg_db/management/commands/load_requirement_data.py +156 -0
  40. endoreg_db/management/commands/load_risk_data.py +56 -0
  41. endoreg_db/mermaid/Overall_flow_patient_finding_intervention.md +10 -0
  42. endoreg_db/mermaid/anonymized_image_annotation.md +20 -0
  43. endoreg_db/mermaid/binary_classification_annotation.md +50 -0
  44. endoreg_db/mermaid/classification.md +8 -0
  45. endoreg_db/mermaid/examination.md +8 -0
  46. endoreg_db/mermaid/findings.md +7 -0
  47. endoreg_db/mermaid/image_classification.md +28 -0
  48. endoreg_db/mermaid/interventions.md +8 -0
  49. endoreg_db/mermaid/morphology.md +8 -0
  50. endoreg_db/mermaid/patient_creation.md +14 -0
  51. endoreg_db/mermaid/video_segmentation_annotation.md +17 -0
  52. endoreg_db/migrations/0009_requirementoperator_requirementsettype_and_more.py +154 -0
  53. endoreg_db/models/__init__.py +20 -0
  54. endoreg_db/models/ai_model/ai_model.py +0 -13
  55. endoreg_db/models/ai_model/model_meta.py +2 -12
  56. endoreg_db/models/data_file/base_classes/abstract_frame.py +0 -2
  57. endoreg_db/models/data_file/base_classes/abstract_pdf.py +0 -9
  58. endoreg_db/models/data_file/base_classes/abstract_video.py +7 -8
  59. endoreg_db/models/data_file/base_classes/utils.py +0 -22
  60. endoreg_db/models/data_file/frame.py +1 -1
  61. endoreg_db/models/data_file/import_classes/raw_pdf.py +5 -11
  62. endoreg_db/models/data_file/import_classes/raw_video.py +6 -4
  63. endoreg_db/models/data_file/video/video.py +3 -3
  64. endoreg_db/models/disease.py +88 -19
  65. endoreg_db/models/event.py +108 -21
  66. endoreg_db/models/examination/examination_indication.py +108 -29
  67. endoreg_db/models/examination/examination_type.py +20 -6
  68. endoreg_db/models/information_source.py +37 -1
  69. endoreg_db/models/laboratory/lab_value.py +83 -32
  70. endoreg_db/models/requirement/__init__.py +11 -0
  71. endoreg_db/models/requirement/requirement.py +325 -0
  72. endoreg_db/models/requirement/requirement_evaluation/__init__.py +134 -0
  73. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +102 -0
  74. endoreg_db/models/requirement/requirement_operator.py +58 -0
  75. endoreg_db/models/requirement/requirement_set.py +127 -0
  76. endoreg_db/models/risk/__init__.py +7 -0
  77. endoreg_db/models/risk/risk.py +72 -0
  78. endoreg_db/models/risk/risk_type.py +55 -0
  79. endoreg_db/serializers/raw_pdf_anony_text_validation.py +137 -0
  80. endoreg_db/serializers/raw_pdf_meta_validation.py +223 -0
  81. endoreg_db/serializers/raw_video_meta_validation.py +163 -1
  82. endoreg_db/serializers/video_segmentation.py +208 -126
  83. endoreg_db/urls.py +127 -14
  84. endoreg_db/utils/__init__.py +43 -0
  85. endoreg_db/utils/dataloader.py +38 -19
  86. endoreg_db/utils/hashs.py +1 -0
  87. endoreg_db/utils/paths.py +86 -0
  88. endoreg_db/views/raw_pdf_anony_text_validation_views.py +95 -0
  89. endoreg_db/views/raw_pdf_meta_validation_views.py +111 -0
  90. endoreg_db/views/raw_video_meta_validation_views.py +128 -18
  91. endoreg_db/views/video_segmentation_views.py +28 -11
  92. endoreg_db/views/views.py +107 -0
  93. {endoreg_db-0.6.2.dist-info → endoreg_db-0.6.4.dist-info}/METADATA +1 -1
  94. {endoreg_db-0.6.2.dist-info → endoreg_db-0.6.4.dist-info}/RECORD +96 -46
  95. endoreg_db/management/commands/load_name_data.py +0 -37
  96. {endoreg_db-0.6.2.dist-info → endoreg_db-0.6.4.dist-info}/WHEEL +0 -0
  97. {endoreg_db-0.6.2.dist-info → endoreg_db-0.6.4.dist-info}/licenses/LICENSE +0 -0
@@ -1,38 +1,148 @@
1
1
  from rest_framework.views import APIView
2
2
  from rest_framework.response import Response
3
3
  from rest_framework import status
4
+ from django.http import FileResponse, Http404
5
+ import mimetypes
6
+ import os
4
7
  from ..models import RawVideoFile
5
- from ..serializers.raw_video_meta_validation import VideoFileForMetaSerializer
8
+ from ..serializers.raw_video_meta_validation import VideoFileForMetaSerializer,SensitiveMetaUpdateSerializer
9
+ from ..models import SensitiveMeta
10
+
6
11
 
7
12
  class VideoFileForMetaView(APIView):
8
13
  """
9
14
  API endpoint to fetch video metadata step-by-step.
10
- If last_id is not provided Returns the first video.
11
- If last_id is given Returns the next available video.
15
+ Uses the serializer to get the first or next available video.
12
16
  """
13
17
 
14
- ##need to change this fucntion , like the previous one
15
-
16
18
  def get(self, request):
17
19
  """
18
- Handles:
19
- First video if last_id is nt in query params.
20
- Next video where id > last_id` if provided.
20
+ - Fetches the **first available video** if `last_id` is NOT provided.
21
+ - Fetches the **next available video** where `id > last_id` if provided.
22
+ - If no video is available, returns a structured error response.
21
23
  """
22
- last_id = request.GET.get("last_id") # Get last_id from query params (e.g., ?last_id=2)
24
+ last_id = request.GET.get("last_id") # Get last_id from query params
25
+
26
+ # Get the next video as a model instance
27
+ video_entry = VideoFileForMetaSerializer.get_next_video(last_id)
28
+
29
+ if video_entry is None:
30
+ return Response({"error": "No more videos available."}, status=status.HTTP_404_NOT_FOUND)
31
+
32
+ serialized_video = VideoFileForMetaSerializer(video_entry, context={'request': request})
33
+
34
+ # Check if required fields are missing
35
+ response_data = serialized_video.data
36
+ missing_fields = {}
37
+
38
+ if response_data.get('file') is None:
39
+ missing_fields['file'] = "No file associated with this entry."
40
+
41
+ if response_data.get('video_url') is None:
42
+ missing_fields['video_url'] = "Video file is missing."
43
+
44
+ if response_data.get('full_video_path') is None:
45
+ missing_fields['full_video_path'] = "No file path found on server."
23
46
 
47
+ if not response_data.get('patient_first_name'):
48
+ missing_fields['patient_first_name'] = "Patient first name is missing."
49
+
50
+ if not response_data.get('patient_last_name'):
51
+ missing_fields['patient_last_name'] = "Patient last name is missing."
52
+
53
+ if not response_data.get('patient_dob'):
54
+ missing_fields['patient_dob'] = "Patient date of birth is missing."
55
+
56
+ if not response_data.get('examination_date'):
57
+ missing_fields['examination_date'] = "Examination date is missing."
58
+
59
+ if response_data.get('duration') is None:
60
+ missing_fields['duration'] = "Unable to determine video duration. The file might be corrupted or unreadable."
61
+
62
+ if missing_fields:
63
+ return Response({"error": "Missing required data.", "details": missing_fields},
64
+ status=status.HTTP_400_BAD_REQUEST)
65
+
66
+ return Response(serialized_video.data, status=status.HTTP_200_OK)
67
+
68
+ def serve_video_file(self, video_entry):
69
+ """
70
+ Streams the video file dynamically.
71
+ """
24
72
  try:
25
- # If last_id is provided, fetch the next video where id > last_id
26
- # id__gt is orm syntax which is equal to SELECT * FROM rawvideofile WHERE id > 2 ORDER BY id ASC LIMIT 1;
73
+ full_video_path = video_entry.file.path # Get file path
27
74
 
28
- query_filter = {} if last_id is None else {"id__gt": int(last_id)}
29
- video_entry = RawVideoFile.objects.select_related("sensitive_meta").filter(**query_filter).order_by('id').first()
75
+ if not os.path.exists(full_video_path):
76
+ raise Http404("Video file not found.")
30
77
 
31
- if not video_entry:
32
- return Response({"message": "No more videos available."}, status=status.HTTP_404_NOT_FOUND)
78
+ mime_type, _ = mimetypes.guess_type(full_video_path) # Detects file type
79
+ response = FileResponse(open(full_video_path, "rb"), content_type=mime_type or "video/mp4")
33
80
 
34
- serializer = VideoFileForMetaSerializer(video_entry)
35
- return Response(serializer.data, status=status.HTTP_200_OK)
81
+ response["Content-Disposition"] = f'inline; filename="{os.path.basename(full_video_path)}"' # Allows direct streaming
82
+
83
+ return response # Sends the video file as a stream
36
84
 
37
85
  except Exception as e:
38
- return Response({"error": f"Internal server error: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
86
+ return Response({"error": f"Internal error: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
87
+
88
+
89
+ def patch(self, request, *args, **kwargs):
90
+ """
91
+ Calls the serializer to update `SensitiveMeta` data.
92
+ """
93
+
94
+ # Ensure all required fields are provided
95
+ required_fields = ["sensitive_meta_id", "patient_first_name", "patient_last_name", "patient_dob", "examination_date"]
96
+ missing_fields = [field for field in required_fields if field not in request.data]
97
+
98
+ if missing_fields:
99
+ return Response({"error": "Missing required fields", "missing_fields": missing_fields}, status=status.HTTP_400_BAD_REQUEST)
100
+
101
+ # Call serializer for validation
102
+ serializer = SensitiveMetaUpdateSerializer(data=request.data, partial=True)
103
+
104
+ if serializer.is_valid():
105
+ # Get the instance and update it
106
+ sensitive_meta = SensitiveMeta.objects.get(id=request.data["sensitive_meta_id"])
107
+ updated_instance = serializer.update(sensitive_meta, serializer.validated_data)
108
+
109
+ return Response({
110
+ "message": "Patient information updated successfully.",
111
+ "updated_data": SensitiveMetaUpdateSerializer(updated_instance).data
112
+ }, status=status.HTTP_200_OK)
113
+
114
+ # Return validation errors
115
+ return Response({"error": "Invalid data.", "details": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
116
+
117
+
118
+ """
119
+ await import('https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js');
120
+ const updatePatientInfo = async () => {
121
+ const updatedData = {
122
+ sensitive_meta_id: 2,
123
+ patient_first_name: "Placeholder",
124
+ patient_last_name: "Placeholder",
125
+ patient_dob: "1994-06-15",
126
+ examination_date: "2024-06-15"
127
+ };
128
+
129
+ try {
130
+ const response = await axios.patch("http://localhost:8000/api/video/update_sensitivemeta/", updatedData, {
131
+ headers: { "Content-Type": "application/json" }
132
+ });
133
+
134
+ console.log("Update Success:", response.data);
135
+ alert("Patient information updated successfully!");
136
+
137
+ return response.data;
138
+ } catch (error) {
139
+ console.error("Update Error:", error.response?.data || error);
140
+ alert("Failed to update patient information.");
141
+ return error.response?.data || { error: "Unknown error" };
142
+ }
143
+ };
144
+
145
+
146
+ updatePatientInfo().then(response => console.log("Final Response:", response));
147
+
148
+ """
@@ -31,9 +31,14 @@ class VideoView(APIView):
31
31
  Returns a list of all available videos along with available labels.
32
32
  Used to populate the video selection dropdown in Vue.js.
33
33
  """
34
+
34
35
  videos = RawVideoFile.objects.all()
35
36
  labels = Label.objects.all() # Fetch all labels
36
37
 
38
+ if not videos.exists():
39
+ return Response({"error": "No videos found in the database."}, status=status.HTTP_404_NOT_FOUND)
40
+
41
+
37
42
  video_serializer = VideoListSerializer(videos, many=True)
38
43
  label_serializer = LabelSerializer(labels, many=True) # Serialize labels
39
44
 
@@ -134,16 +139,28 @@ class UpdateLabelSegmentsView(APIView):
134
139
 
135
140
  def put(self, request, video_id, label_id):
136
141
  """
137
- Handles PUT request to update or create label segments.
142
+ Updates segments for a given video & label.
138
143
  """
139
- serializer = LabelSegmentUpdateSerializer(data=request.data)
140
144
 
141
- if serializer.is_valid():
142
- result = serializer.save()
143
- return Response({
144
- "message": "Segments updated successfully",
145
- "updated_segments": result["updated_segments"],
146
- "new_segments": result["new_segments"]
147
- }, status=status.HTTP_200_OK)
148
-
149
- return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
145
+ # Ensure required fields are provided
146
+ required_fields = ["video_id", "label_id", "segments"]
147
+ missing_fields = [field for field in required_fields if field not in request.data]
148
+
149
+ if missing_fields:
150
+ return Response({"error": "Missing required fields", "missing": missing_fields}, status=status.HTTP_400_BAD_REQUEST)
151
+
152
+ # Validate input data
153
+ serializer = LabelSegmentUpdateSerializer(data=request.data, partial=True)
154
+
155
+ if not serializer.is_valid():
156
+ return Response({"error": "Invalid segment data", "details": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
157
+
158
+ # Process and save segment updates
159
+ result = serializer.save()
160
+
161
+ return Response({
162
+ "message": "Segments updated successfully.",
163
+ "updated_segments": result["updated_segments"],
164
+ "new_segments": result["new_segments"],
165
+ "deleted_segments": result["deleted_segments"]
166
+ }, status=status.HTTP_200_OK)
@@ -0,0 +1,107 @@
1
+ #for keycloak need to make separate onne,demo
2
+ from rest_framework.permissions import IsAuthenticated
3
+ from rest_framework.views import APIView
4
+ from rest_framework.response import Response
5
+
6
+ from django.shortcuts import redirect, render
7
+ from django.conf import settings
8
+ from urllib.parse import urlencode
9
+ import requests
10
+
11
+ """
12
+ User hits /api/videos/
13
+ Middleware checks for token; if missing, redirects to /login/
14
+ /login/ redirects to Keycloak
15
+ User logs in → Keycloak sends them back to /login/callback/
16
+ /login/callback/ exchanges code for token, stores it in session
17
+ User is redirected to /api/videos/ again
18
+ Middleware now sees token, verifies it, injects user
19
+ DRF view (VideoView) is allowed to execute and returns data
20
+ """
21
+ class VideoView(APIView):
22
+ permission_classes = [IsAuthenticated] #This uses DRF permissions to ensure request.user.is_authenticated == True.
23
+
24
+ def get(self, request):
25
+ """
26
+ We already inject a mock user in the middleware, so this will pass if the middleware succeeded.
27
+ Returns a message including the Keycloak username.
28
+ """
29
+ username = getattr(request.user, 'preferred_username', 'Unknown')
30
+ return Response({"message": f"🎥 Hello, {username}. You are viewing protected videos!"})
31
+
32
+
33
+ def keycloak_login(request):
34
+
35
+ """
36
+ - This gets triggered when middleware redirects to /login/.
37
+ """
38
+ redirect_uri = request.build_absolute_uri('/login/callback/')
39
+ print("Redirect URI:", redirect_uri)
40
+ auth_url = f"{settings.KEYCLOAK_SERVER_URL}/realms/{settings.KEYCLOAK_REALM}/protocol/openid-connect/auth"
41
+
42
+ #OAuth2 Authorization Code Flow
43
+ params = {
44
+ "client_id": settings.KEYCLOAK_CLIENT_ID,
45
+ "response_type": "code",
46
+ "scope": "openid",
47
+ "redirect_uri": redirect_uri,
48
+ }
49
+ # Redirect user to Keycloak login page.
50
+ return redirect(f"{auth_url}?{urlencode(params)}")
51
+
52
+
53
+ import json
54
+ from django.http import HttpResponse
55
+
56
+ from django.http import HttpResponse, HttpResponseRedirect
57
+ import json
58
+
59
+ def keycloak_callback(request):
60
+
61
+ #User lands here after login (Keycloak redirects here with code).
62
+ code = request.GET.get("code")
63
+ if not code:
64
+ return HttpResponse(" No authorization code provided.", status=400)
65
+
66
+ # Exchanges the code for an access_token.
67
+ token_url = f"{settings.KEYCLOAK_SERVER_URL}/realms/{settings.KEYCLOAK_REALM}/protocol/openid-connect/token"
68
+ redirect_uri = request.build_absolute_uri('/login/callback/')
69
+
70
+ data = {
71
+ "grant_type": "authorization_code",
72
+ "code": code,
73
+ "client_id": settings.KEYCLOAK_CLIENT_ID,
74
+ "client_secret": settings.KEYCLOAK_CLIENT_SECRET,
75
+ "redirect_uri": redirect_uri,
76
+ }
77
+
78
+ try:
79
+ response = requests.post(token_url, data=data)
80
+
81
+ print("Token Response Status:", response.status_code)
82
+ print(" Token Response Body:", response.text)
83
+
84
+ if response.status_code != 200:
85
+ return HttpResponse(
86
+ f"<h2> Token exchange failed</h2><pre>{response.text}</pre>",
87
+ status=500
88
+ )
89
+
90
+ token_data = response.json()
91
+
92
+ if "access_token" not in token_data:
93
+ return HttpResponse(" Access token missing in response.", status=500)
94
+
95
+ # Stores the token in Django session. Middleware will use this on the next request.
96
+ request.session["access_token"] = token_data["access_token"]
97
+ request.session["refresh_token"] = token_data["refresh_token"]
98
+ print("Refresh Token:", request.session.get("refresh_token"))
99
+
100
+ return redirect("/api/videos/")
101
+
102
+ except Exception as e:
103
+ return HttpResponse(f" Exception during token exchange: {str(e)}", status=500)
104
+
105
+
106
+ def public_home(request):
107
+ return HttpResponse("just for demo - This is a public home page — no login required.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: endoreg-db
3
- Version: 0.6.2
3
+ Version: 0.6.4
4
4
  Summary: EndoReg Db Django App
5
5
  Project-URL: Homepage, https://info.coloreg.de
6
6
  Project-URL: Repository, https://github.com/wg-lux/endoreg-db