endoreg-db 0.6.3__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 (78) 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/examination/examinations/data.yaml +22 -21
  5. endoreg_db/data/examination/type/data.yaml +12 -0
  6. endoreg_db/data/examination_indication/endoscopy.yaml +417 -1
  7. endoreg_db/data/examination_indication_classification/endoscopy.yaml +157 -5
  8. endoreg_db/data/finding/data.yaml +18 -11
  9. endoreg_db/data/finding_intervention/endoscopy.yaml +26 -121
  10. endoreg_db/data/finding_intervention/endoscopy_colonoscopy.yaml +163 -0
  11. endoreg_db/data/finding_intervention/endoscopy_egd.yaml +128 -0
  12. endoreg_db/data/finding_intervention/endoscopy_ercp.yaml +32 -0
  13. endoreg_db/data/finding_intervention/endoscopy_eus_lower.yaml +9 -0
  14. endoreg_db/data/finding_intervention/endoscopy_eus_upper.yaml +36 -0
  15. endoreg_db/data/information_source/endoscopy_guidelines.yaml +7 -0
  16. endoreg_db/data/medication_indication/anticoagulation.yaml +4 -4
  17. endoreg_db/data/pdf_type/data.yaml +9 -16
  18. endoreg_db/data/requirement/colonoscopy_indications.yaml +56 -0
  19. endoreg_db/data/requirement/disease_cardiovascular.yaml +79 -0
  20. endoreg_db/data/requirement/disease_classification_choice_cardiovascular.yaml +38 -0
  21. endoreg_db/data/requirement/disease_hepatology.yaml +12 -0
  22. endoreg_db/data/requirement/disease_misc.yaml +12 -0
  23. endoreg_db/data/requirement/disease_renal.yaml +80 -0
  24. endoreg_db/data/requirement/event_cardiology.yaml +251 -0
  25. endoreg_db/data/requirement/lab_value.yaml +120 -0
  26. endoreg_db/data/requirement_operator/lab_operators.yaml +128 -0
  27. endoreg_db/data/requirement_operator/model_operators.yaml +90 -0
  28. endoreg_db/data/requirement_set/endoscopy_bleeding_risk.yaml +12 -0
  29. endoreg_db/data/requirement_set_type/data.yaml +20 -0
  30. endoreg_db/data/requirement_type/requirement_types.yaml +83 -0
  31. endoreg_db/data/risk/bleeding.yaml +26 -0
  32. endoreg_db/data/risk/thrombosis.yaml +37 -0
  33. endoreg_db/data/risk_type/data.yaml +27 -0
  34. endoreg_db/data/unit/time.yaml +36 -1
  35. endoreg_db/management/commands/load_base_db_data.py +14 -1
  36. endoreg_db/management/commands/load_center_data.py +46 -21
  37. endoreg_db/management/commands/load_examination_indication_data.py +49 -27
  38. endoreg_db/management/commands/load_requirement_data.py +156 -0
  39. endoreg_db/management/commands/load_risk_data.py +56 -0
  40. endoreg_db/migrations/0009_requirementoperator_requirementsettype_and_more.py +154 -0
  41. endoreg_db/models/__init__.py +20 -0
  42. endoreg_db/models/ai_model/ai_model.py +0 -13
  43. endoreg_db/models/ai_model/model_meta.py +2 -12
  44. endoreg_db/models/data_file/base_classes/abstract_frame.py +0 -2
  45. endoreg_db/models/data_file/base_classes/abstract_pdf.py +0 -9
  46. endoreg_db/models/data_file/base_classes/abstract_video.py +7 -8
  47. endoreg_db/models/data_file/base_classes/utils.py +0 -22
  48. endoreg_db/models/data_file/frame.py +1 -1
  49. endoreg_db/models/data_file/import_classes/raw_pdf.py +5 -11
  50. endoreg_db/models/data_file/import_classes/raw_video.py +6 -4
  51. endoreg_db/models/data_file/video/video.py +3 -3
  52. endoreg_db/models/disease.py +88 -19
  53. endoreg_db/models/event.py +108 -21
  54. endoreg_db/models/examination/examination_indication.py +108 -29
  55. endoreg_db/models/examination/examination_type.py +20 -6
  56. endoreg_db/models/information_source.py +37 -1
  57. endoreg_db/models/laboratory/lab_value.py +83 -32
  58. endoreg_db/models/requirement/__init__.py +11 -0
  59. endoreg_db/models/requirement/requirement.py +325 -0
  60. endoreg_db/models/requirement/requirement_evaluation/__init__.py +134 -0
  61. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +102 -0
  62. endoreg_db/models/requirement/requirement_operator.py +58 -0
  63. endoreg_db/models/requirement/requirement_set.py +127 -0
  64. endoreg_db/models/risk/__init__.py +7 -0
  65. endoreg_db/models/risk/risk.py +72 -0
  66. endoreg_db/models/risk/risk_type.py +55 -0
  67. endoreg_db/serializers/video_segmentation.py +12 -25
  68. endoreg_db/urls.py +17 -3
  69. endoreg_db/utils/__init__.py +43 -0
  70. endoreg_db/utils/dataloader.py +38 -19
  71. endoreg_db/utils/hashs.py +1 -0
  72. endoreg_db/utils/paths.py +86 -0
  73. endoreg_db/views/views.py +107 -0
  74. {endoreg_db-0.6.3.dist-info → endoreg_db-0.6.4.dist-info}/METADATA +1 -1
  75. {endoreg_db-0.6.3.dist-info → endoreg_db-0.6.4.dist-info}/RECORD +77 -42
  76. endoreg_db/management/commands/load_name_data.py +0 -37
  77. {endoreg_db-0.6.3.dist-info → endoreg_db-0.6.4.dist-info}/WHEEL +0 -0
  78. {endoreg_db-0.6.3.dist-info → endoreg_db-0.6.4.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,102 @@
1
+ # Currently those strings MUST match the ones
2
+ # in the requirement_type data definitions
3
+ from collections import namedtuple
4
+ from endoreg_db.models import (
5
+ Requirement,
6
+ RequirementType,
7
+ PatientExamination,
8
+ PatientFindingIntervention,
9
+ Examination,
10
+ ExaminationIndication,
11
+ Disease,
12
+ DiseaseClassificationChoice,
13
+ PatientEvent,
14
+ PatientFinding,
15
+ PatientFindingMorphology,
16
+ )
17
+ from typing import List
18
+ from icecream import ic
19
+ # Requirement has RequirementTypes
20
+ # Requirement has RequirementOperators
21
+ # For each Operator/Type pair, there must be a custom function
22
+ # for evaluation
23
+
24
+ OperatorTypeTuple = namedtuple("OperatorTypeTuple", ["operator", "requirement_type"])
25
+
26
+
27
+ data_model_dict = {
28
+ "patient_examination": PatientExamination,
29
+ "finding_intervention": PatientFindingIntervention,
30
+ "finding_interventions": List[PatientFindingIntervention],
31
+ "examination": Examination,
32
+ "examination_indication": ExaminationIndication,
33
+ "disease": Disease,
34
+ "disease_classification_choice": DiseaseClassificationChoice,
35
+ "event": PatientEvent,
36
+ "finding": PatientFinding,
37
+ "finding_morphology": PatientFindingMorphology,
38
+ # Add more mappings as needed
39
+ }
40
+
41
+
42
+ def evaluate_operator_type_tuple(
43
+ operator_type_tuple: OperatorTypeTuple, data: object, **kwargs
44
+ ):
45
+ """
46
+ Evaluates the requirement type and operator tuple against the provided data.
47
+
48
+ Args:
49
+ operator_type_tuple (OperatorTypeTuple): Tuple containing requirement type and operator.
50
+ data (object): The data to evaluate.
51
+ **kwargs: Additional keyword arguments.
52
+
53
+ Returns:
54
+ bool: True if the evaluation is successful, False otherwise.
55
+ """
56
+ # data = kwargs.get(name, None)
57
+
58
+ # # make sure data type matches the model for the req_type name
59
+ # if data is None:
60
+ # raise ValueError(f"No data found for requirement type: {name}")
61
+ # if name not in data_model_dict:
62
+ # raise ValueError(f"Unknown requirement type: {name}")
63
+
64
+ # model = data_model_dict[name]
65
+ # if not isinstance(data, model):
66
+ # raise TypeError(
67
+ # f"Data type mismatch for {name}: expected {model}, got {type(data)}"
68
+ # )
69
+
70
+ requirement_type = operator_type_tuple.requirement_type
71
+ operator = operator_type_tuple.operator
72
+
73
+ # Call the appropriate function based on the requirement type and operator
74
+ # This is a placeholder; actual implementation will depend on your logic
75
+ return True # Replace with actual evaluation logic
76
+
77
+
78
+ def evaluate_requirement(requirement: Requirement, **kwargs):
79
+ requirement_types = requirement.requirement_types.all()
80
+ requirement_operators = requirement.operators.all()
81
+ operator_return_values = {}
82
+ operator_evaluation_results = {}
83
+
84
+ for requirement_operator in requirement_operators:
85
+ # Create tuples
86
+ requirement_type_operator_tuples = [
87
+ OperatorTypeTuple(requirement_operator, rt) for ro in requirement_types
88
+ ]
89
+
90
+ eval_result = [
91
+ evaluate_operator_type_tuple(t, **kwargs)
92
+ for t in requirement_type_operator_tuples
93
+ ]
94
+
95
+ operator_evaluation_results[requirement_operator.name] = eval_result
96
+
97
+ # TODO FIX
98
+ operator_return_values[requirement_operator.name] = (
99
+ requirement_operator.evaluate_return_values()
100
+ )
101
+
102
+ return operator_evaluation_results, operator_return_values
@@ -0,0 +1,58 @@
1
+ from django.db import models
2
+ from typing import TYPE_CHECKING
3
+
4
+
5
+ class RequirementOperatorManager(models.Manager):
6
+ def get_by_natural_key(self, name):
7
+ """
8
+ Retrieve a RequirementOperator instance by its natural key.
9
+
10
+ Args:
11
+ name (str): The unique name representing the natural key.
12
+
13
+ Returns:
14
+ RequirementOperator: The model instance matching the provided name.
15
+ """
16
+ return self.get(name=name)
17
+
18
+
19
+ class RequirementOperator(models.Model):
20
+ """
21
+ A class representing a requirement operator.
22
+
23
+ Attributes:
24
+ name (str): The name of the requirement operator.
25
+ name_de (str): The German name of the requirement operator.
26
+ name_en (str): The English name of the requirement operator.
27
+ description (str): A description of the requirement operator.
28
+ """
29
+
30
+ name = models.CharField(max_length=100, unique=True)
31
+ name_de = models.CharField(max_length=100, blank=True, null=True)
32
+ name_en = models.CharField(max_length=100, blank=True, null=True)
33
+ description = models.TextField(blank=True, null=True)
34
+
35
+ objects = RequirementOperatorManager()
36
+
37
+ if TYPE_CHECKING:
38
+ from endoreg_db.models.requirement.requirement import Requirement
39
+
40
+ requirements: models.QuerySet[Requirement]
41
+
42
+ def natural_key(self):
43
+ """
44
+ Return the natural key for the requirement operator.
45
+
46
+ This method returns a tuple containing the operator's name, which serves as its
47
+ natural key for serialization and unique identification within the system.
48
+ """
49
+ return (self.name,)
50
+
51
+ def __str__(self):
52
+ """
53
+ Return the string representation of the requirement operator.
54
+
55
+ Returns:
56
+ str: The name attribute of the operator.
57
+ """
58
+ return str(self.name)
@@ -0,0 +1,127 @@
1
+ from django.db import models
2
+ from typing import TYPE_CHECKING
3
+
4
+
5
+ class RequirementSetTypeManager(models.Manager):
6
+ """
7
+ Manager for RequirementSetType with custom query methods.
8
+ """
9
+
10
+ def get_by_natural_key(self, name: str) -> "RequirementSetType":
11
+ """
12
+ Retrieves a RequirementSetType instance using its natural key.
13
+
14
+ Args:
15
+ name (str): The unique name that serves as the natural key.
16
+
17
+ Returns:
18
+ RequirementSetType: The matching RequirementSetType instance.
19
+ """
20
+ return self.get(name=name)
21
+
22
+
23
+ class RequirementSetType(models.Model):
24
+ """
25
+ A class representing a type of requirement set.
26
+
27
+ Attributes:
28
+ name (str): The name of the requirement set type.
29
+ name_de (str): The German name of the requirement set type.
30
+ name_en (str): The English name of the requirement set type.
31
+ """
32
+
33
+ name = models.CharField(max_length=100, unique=True)
34
+ name_de = models.CharField(max_length=100, blank=True, null=True)
35
+ name_en = models.CharField(max_length=100, blank=True, null=True)
36
+ description = models.TextField(blank=True, null=True)
37
+
38
+ objects = RequirementSetTypeManager()
39
+
40
+ if TYPE_CHECKING:
41
+ from endoreg_db.models.requirement import RequirementType
42
+
43
+ requirement_types: models.QuerySet[RequirementType]
44
+
45
+ def natural_key(self):
46
+ """
47
+ Return the natural key tuple for the instance.
48
+
49
+ Returns:
50
+ tuple: A one-element tuple containing the instance's name, used as its natural key.
51
+ """
52
+ return (self.name,)
53
+
54
+
55
+ class RequirementSetManager(models.Manager):
56
+ def get_by_natural_key(self, name):
57
+ """
58
+ Retrieves a model instance by its natural key.
59
+
60
+ Args:
61
+ name: The natural key value, typically corresponding to the model's unique name.
62
+
63
+ Returns:
64
+ The model instance whose name matches the provided natural key.
65
+ """
66
+ return self.get(name=name)
67
+
68
+
69
+ class RequirementSet(models.Model):
70
+ """
71
+ A class representing a set of requirements.
72
+
73
+ Attributes:
74
+ name (str): The name of the requirement set.
75
+ name_de (str): The German name of the requirement set.
76
+ name_en (str): The English name of the requirement set.
77
+ description (str): A description of the requirement set.
78
+ """
79
+
80
+ name = models.CharField(max_length=100, unique=True)
81
+ name_de = models.CharField(max_length=100, blank=True, null=True)
82
+ name_en = models.CharField(max_length=100, blank=True, null=True)
83
+ description = models.TextField(blank=True, null=True)
84
+ requirements = models.ManyToManyField(
85
+ "Requirement",
86
+ blank=True,
87
+ related_name="requirement_sets",
88
+ )
89
+ links_to_sets = models.ManyToManyField(
90
+ "RequirementSet",
91
+ blank=True,
92
+ related_name="links_from_sets",
93
+ )
94
+ requirement_set_type = models.ForeignKey(
95
+ "RequirementSetType",
96
+ on_delete=models.CASCADE,
97
+ related_name="requirement_sets",
98
+ blank=True,
99
+ null=True,
100
+ )
101
+ information_sources = models.ManyToManyField(
102
+ "InformationSource",
103
+ related_name="requirement_sets",
104
+ blank=True,
105
+ )
106
+ objects = RequirementSetManager()
107
+
108
+ if TYPE_CHECKING:
109
+ from endoreg_db.models import Requirement, InformationSource
110
+
111
+ requirements: models.QuerySet[Requirement]
112
+ information_source: InformationSource
113
+ requirement_set_type: RequirementSetType
114
+ linked_sets: models.QuerySet["RequirementSet"]
115
+
116
+ def natural_key(self):
117
+ """Return the natural key as a tuple containing the instance's name."""
118
+ return (self.name,)
119
+
120
+ def __str__(self):
121
+ """
122
+ Return the string representation of the requirement set.
123
+
124
+ Returns:
125
+ str: The name of the requirement set.
126
+ """
127
+ return str(self.name)
@@ -0,0 +1,7 @@
1
+ from .risk import Risk
2
+ from .risk_type import RiskType
3
+
4
+ __all__ = [
5
+ "Risk",
6
+ "RiskType",
7
+ ]
@@ -0,0 +1,72 @@
1
+ from django.db import models
2
+ from typing import List, TYPE_CHECKING
3
+
4
+
5
+ class RiskManager(models.Manager):
6
+ def get_by_natural_key(self, name):
7
+ """
8
+ Retrieve a risk instance using its natural key.
9
+
10
+ Args:
11
+ name: The unique name identifying the risk instance.
12
+
13
+ Returns:
14
+ The risk instance with the matching name.
15
+ """
16
+ return self.get(name=name)
17
+
18
+
19
+ class Risk(models.Model):
20
+ """
21
+ A class representing a risk.
22
+
23
+ Attributes:
24
+ name (str): The name of the risk.
25
+ name_de (str): The German name of the risk.
26
+ name_en (str): The English name of the risk.
27
+ description (str): A description of the risk.
28
+ """
29
+
30
+ name = models.CharField(max_length=100, unique=True)
31
+ name_de = models.CharField(max_length=100, blank=True, null=True)
32
+ name_en = models.CharField(max_length=100, blank=True, null=True)
33
+ description = models.TextField(blank=True, null=True)
34
+
35
+ risk_value = models.FloatField(
36
+ blank=True,
37
+ null=True,
38
+ help_text="Risk value for the risk. If not set, the risk is not used in calculations.",
39
+ )
40
+
41
+ risk_type = models.ForeignKey(
42
+ "RiskType",
43
+ on_delete=models.CASCADE,
44
+ related_name="risks",
45
+ blank=True,
46
+ null=True,
47
+ )
48
+
49
+ objects = RiskManager()
50
+
51
+ if TYPE_CHECKING:
52
+ from endoreg_db.models.risk.risk_type import RiskType
53
+
54
+ risk_types: RiskType
55
+
56
+ def natural_key(self):
57
+ """
58
+ Return a tuple containing the natural key of the risk instance.
59
+
60
+ The tuple consists of the unique 'name' attribute, which enables natural key lookups
61
+ and serialization within Django.
62
+ """
63
+ return (self.name,)
64
+
65
+ def __str__(self):
66
+ """
67
+ Return the string representation of the risk.
68
+
69
+ Returns:
70
+ str: The risk's name.
71
+ """
72
+ return str(self.name)
@@ -0,0 +1,55 @@
1
+ from django.db import models
2
+ from typing import TYPE_CHECKING
3
+
4
+
5
+ class RiskTypeManager(models.Manager):
6
+ def get_by_natural_key(self, name):
7
+ """
8
+ Retrieves a RiskType instance using its natural key.
9
+
10
+ Args:
11
+ name (str): The unique name identifying the RiskType instance.
12
+
13
+ Returns:
14
+ RiskType: The matching instance with the provided name.
15
+ """
16
+ return self.get(name=name)
17
+
18
+
19
+ class RiskType(models.Model):
20
+ """
21
+ A class representing a risk type.
22
+
23
+ Attributes:
24
+ name (str): The name of the risk type.
25
+ name_de (str): The German name of the risk type.
26
+ name_en (str): The English name of the risk type.
27
+ description (str): A description of the risk type.
28
+ """
29
+
30
+ name = models.CharField(max_length=100, unique=True)
31
+ name_de = models.CharField(max_length=100, blank=True, null=True)
32
+ name_en = models.CharField(max_length=100, blank=True, null=True)
33
+ description = models.TextField(blank=True, null=True)
34
+
35
+ objects = RiskTypeManager()
36
+
37
+ if TYPE_CHECKING:
38
+ from endoreg_db.models.risk.risk import Risk
39
+
40
+ risks: models.QuerySet[Risk]
41
+
42
+ def natural_key(self):
43
+ """
44
+ Return the natural key for this risk type.
45
+
46
+ This method returns a tuple containing only the risk type's unique name, which is used
47
+ to identify the instance naturally.
48
+ """
49
+ return (self.name,)
50
+
51
+ def __str__(self):
52
+ """
53
+ Return the risk type's name as its string representation.
54
+ """
55
+ return str(self.name)
@@ -112,14 +112,12 @@ class VideoFileSerializer(serializers.ModelSerializer):
112
112
  obj.file.name
113
113
  ).strip() # Only return the file path, no URL,#obj.file returning a FieldFile object instead of a string
114
114
 
115
- """The error "muxer does not support non-seekable output"
116
- happens because MP4 format requires seeking, but FFmpeg does not support writing MP4 directly to a non-seekable stream (like STDOUT)."""
117
-
118
115
  def get_full_video_path(self, obj):
119
116
  """
120
117
  Constructs the absolute file path dynamically.
121
118
  - Uses the actual storage directory (`/home/admin/test-data/`)
122
119
  """
120
+ from ..utils import STORAGE_DIR
123
121
  if not obj.file:
124
122
  return {"error": "No video file associated with this entry"}
125
123
 
@@ -129,16 +127,7 @@ class VideoFileSerializer(serializers.ModelSerializer):
129
127
  "error": "Video file path is empty or invalid"
130
128
  } # none might cause, 500 error, Handle edge case where the file name is empty
131
129
 
132
- print("-----------------------------------------")
133
- # pseudo_dir = settings.PSEUDO_DIR
134
- # print(f"Using pseudo directory: {pseudo_dir}")
135
-
136
- # full path using the actual storage directory~
137
- # actual_storage_dir = Path("~/test-data") # need to change
138
- actual_storage_dir = Path("/home/admin/test-data") # need to change
139
- # actual_storage_dir = pseudo_dir
140
- full_path = actual_storage_dir / video_relative_path
141
- # full_path = Path("/home/admin/test-data/video/lux-gastro-video.mp4")
130
+ full_path = STORAGE_DIR / video_relative_path
142
131
 
143
132
  return (
144
133
  str(full_path)
@@ -354,14 +343,16 @@ class LabelSegmentUpdateSerializer(serializers.Serializer):
354
343
 
355
344
  def save(self):
356
345
  """
357
- Updates, inserts, and deletes label segments to ensure database consistency.
358
-
359
- Steps:
360
- 1. Fetch all existing segments for the given `video_id` and `label_id`.
361
- 2. Compare existing segments with the new ones from the frontend.
362
- 3. Update segments where `start_frame_number` exists but `end_frame_number` has changed.
363
- 4. Insert new segments that are not already in the database.
364
- 5. Delete segments that exist in the database but are missing from the frontend data.
346
+ Synchronizes label segments with updated frontend data.
347
+
348
+ This method compares the incoming segments with the current database entries for a given video and label.
349
+ It updates segments with modified end frame numbers, inserts new segments, and deletes existing segments
350
+ that are not present in the provided data. All operations are performed within a transaction to ensure
351
+ database consistency. A validation error is raised if no prediction metadata is found for the video.
352
+
353
+ Returns:
354
+ dict: A dictionary containing serialized updated segments, serialized new segments, and the count
355
+ of deleted segments.
365
356
  """
366
357
 
367
358
  video_id = self.validated_data["video_id"]
@@ -391,10 +382,6 @@ class LabelSegmentUpdateSerializer(serializers.Serializer):
391
382
  (float(seg.start_frame_number), float(seg.end_frame_number)): seg
392
383
  for seg in existing_segments
393
384
  }
394
- existing_segments_dict = {
395
- (float(seg.start_frame_number), float(seg.end_frame_number)): seg
396
- for seg in existing_segments
397
- }
398
385
 
399
386
  # Prepare lists for batch processing
400
387
  updated_segments = [] # Stores segments that need to be updated
endoreg_db/urls.py CHANGED
@@ -7,7 +7,13 @@ from .views.patient_views import (
7
7
  get_morphology_choices,
8
8
  )
9
9
  from .views.csrf import csrf_token_view
10
- #from .views.feature_selection_view import FetchSingleFramePredictionView // its implemented in ando-ai other project need to add here
10
+ # endoreg_db_production/endoreg_db/urls.py
11
+ from endoreg_db.views.views import (
12
+ VideoView,
13
+ keycloak_login,
14
+ keycloak_callback,
15
+ public_home,
16
+ )#from .views.feature_selection_view import FetchSingleFramePredictionView // its implemented in ando-ai other project need to add here
11
17
  from .views.video_segmentation_views import VideoView, VideoLabelView,UpdateLabelSegmentsView
12
18
  from .views.views_for_timeline import video_timeline_view
13
19
  from .views.raw_video_meta_validation_views import VideoFileForMetaView, VideoFileForMetaView
@@ -34,7 +40,7 @@ urlpatterns = [
34
40
  # will be displayed on the frontend using route(api/video/<int:video_id>/).
35
41
  # once label is selected from the dropdown,using its name, details can be fetched from rawvideofile using route("api/video/<int:video_id>/label/<str:label_name>/)
36
42
  #If editing is required, a form will be available for each label. This form dynamically updates when the selected label changes. It will display all segments associated with the selected label, each with a delete option. Below these segments, there may be a button for adding more segments.
37
- #If any values in the form are modified, the updated data will be saved in the database table.
43
+ #If any values in the form are modified, the updated data will be saved in the dafrom .views import VideoView, keycloak_login, keycloak_callbacktabase table.
38
44
 
39
45
 
40
46
 
@@ -45,7 +51,15 @@ urlpatterns = [
45
51
  # Example request: GET /api/videos/
46
52
  # Response: Returns a JSON list of videos (id, file path, video_url, sequences, label_names).
47
53
  # it also fetch the label from teh label table
48
- path("api/videos/", VideoView.as_view(), name="video_list"),
54
+
55
+ # for kaycloak:-
56
+ # /api/videos/ , protected API
57
+ # /login/ , sends user to Keycloak login page
58
+ # /login/callback/ , where Keycloak sends the user after login
59
+ path('', public_home, name='public_home'),
60
+ path('api/videos/', VideoView.as_view(), name='video_list'),
61
+ path('login/', keycloak_login, name='keycloak_login'),
62
+ path('login/callback/', keycloak_callback, name='keycloak_callback'),
49
63
 
50
64
  #need to change this route
51
65
  #path('api/prediction/', FetchSingleFramePredictionView.as_view(), name='fetch-single-frame-prediction'),#.as_view() converts the class-based view into a function that Django can use for routing.
@@ -11,6 +11,7 @@ from .hashs import (
11
11
  get_examiner_hash,
12
12
  get_video_hash,
13
13
  get_patient_examination_hash,
14
+ DJANGO_NAME_SALT
14
15
  )
15
16
  from .names import (
16
17
  create_mock_patient_name,
@@ -18,6 +19,28 @@ from .names import (
18
19
  guess_name_gender,
19
20
  )
20
21
 
22
+ from .paths import (
23
+ STORAGE_DIR,
24
+ FRAME_DIR,
25
+ VIDEO_DIR,
26
+ RAW_VIDEO_DIR,
27
+ RAW_FRAME_DIR,
28
+ TEST_RUN,
29
+ TEST_RUN_FRAME_NUMBER,
30
+ FRAME_PROCESSING_BATCH_SIZE,
31
+ WEIGHTS_DIR,
32
+ WEIGHTS_DIR_NAME,
33
+ FRAME_DIR_NAME,
34
+ VIDEO_DIR_NAME,
35
+ STORAGE_DIR_NAME,
36
+ RAW_FRAME_DIR_NAME,
37
+ RAW_VIDEO_DIR_NAME,
38
+ PDF_DIR_NAME,
39
+ PDF_DIR,
40
+ RAW_PDF_DIR,
41
+ RAW_PDF_DIR_NAME,
42
+ )
43
+
21
44
  __all__ = [
22
45
  "load_model_data_from_yaml",
23
46
  "collect_center_names",
@@ -33,4 +56,24 @@ __all__ = [
33
56
  "get_pdf_hash",
34
57
  "get_video_hash",
35
58
  "get_patient_examination_hash",
59
+ "STORAGE_DIR",
60
+ "FRAME_DIR",
61
+ "VIDEO_DIR",
62
+ "RAW_VIDEO_DIR",
63
+ "RAW_FRAME_DIR",
64
+ "TEST_RUN",
65
+ "TEST_RUN_FRAME_NUMBER",
66
+ "FRAME_PROCESSING_BATCH_SIZE",
67
+ "DJANGO_NAME_SALT",
68
+ "WEIGHTS_DIR",
69
+ "WEIGHTS_DIR_NAME",
70
+ "FRAME_DIR_NAME",
71
+ "VIDEO_DIR_NAME",
72
+ "STORAGE_DIR_NAME",
73
+ "RAW_FRAME_DIR_NAME",
74
+ "RAW_VIDEO_DIR_NAME",
75
+ "RAW_PDF_DIR_NAME",
76
+ "PDF_DIR_NAME",
77
+ "PDF_DIR",
78
+ "RAW_PDF_DIR",
36
79
  ]
@@ -38,15 +38,22 @@ def load_data_with_foreign_keys(
38
38
  command, model, yaml_data, foreign_keys, foreign_key_models, verbose
39
39
  ):
40
40
  """
41
- Load data handling foreign keys and many-to-many relationships.
42
-
43
- Args:
44
- command: Command object for stdout writing.
45
- model: The Django model for the data.
46
- yaml_data: Data loaded from YAML.
47
- foreign_keys: List of foreign keys.
48
- foreign_key_models: Corresponding models for the foreign keys.
49
- verbose: Boolean indicating whether to print verbose output.
41
+ Load YAML data into Django model instances with FK and M2M support.
42
+
43
+ Processes each YAML entry to create or update a model instance. For each entry, the
44
+ function extracts field data and uses the presence of a 'name' field to decide whether
45
+ to update an existing instance or create a new one. Foreign key fields listed in
46
+ foreign_keys are handled by retrieving related objects via natural keys. When a field
47
+ contains a list, it is treated as a many-to-many relationship and the corresponding
48
+ objects are set after the instance is saved. Missing or unresolved foreign keys trigger
49
+ warnings if verbose output is enabled.
50
+
51
+ Parameters:
52
+ model: The Django model class representing the data.
53
+ yaml_data: A list of dictionaries representing YAML entries.
54
+ foreign_keys: A list of foreign key field names to process from each entry.
55
+ foreign_key_models: The corresponding Django model classes for each foreign key.
56
+ verbose: If True, prints detailed output and warnings during processing.
50
57
  """
51
58
  for entry in yaml_data:
52
59
  fields = entry.get("fields", {})
@@ -56,7 +63,10 @@ def load_data_with_foreign_keys(
56
63
 
57
64
  # Handle foreign keys and many-to-many relationships
58
65
  for fk_field, fk_model in zip(foreign_keys, foreign_key_models):
59
- # print(fk_field, fk_model)
66
+ # Skip fields that are not in the data
67
+ if fk_field not in fields:
68
+ continue
69
+
60
70
  target_keys = fields.pop(fk_field, None)
61
71
 
62
72
  # Ensure the foreign key exists
@@ -73,18 +83,20 @@ def load_data_with_foreign_keys(
73
83
  if isinstance(target_keys, list): # Assume many-to-many relationship
74
84
  related_objects = []
75
85
  for key in target_keys:
76
- obj, created = fk_model.objects.get_or_create(name=key)
77
- if created and verbose:
78
- command.stdout.write(
79
- command.style.SUCCESS(f"Created {fk_model.__name__} {key}")
80
- )
86
+ try:
87
+ obj = fk_model.objects.get_by_natural_key(key)
88
+ except ObjectDoesNotExist:
89
+ if verbose:
90
+ command.stdout.write(
91
+ command.style.WARNING(
92
+ f"{fk_model.__name__} with key {key} not found"
93
+ )
94
+ )
95
+ continue
81
96
  related_objects.append(obj)
82
97
  m2m_relationships[fk_field] = related_objects
83
98
  else: # Single foreign key relationship
84
99
  try:
85
- if model == "endoreg_db.case_template_rule":
86
- # print(fk_model, target_keys)
87
- pass
88
100
  obj = fk_model.objects.get_by_natural_key(target_keys)
89
101
  except ObjectDoesNotExist:
90
102
  if verbose:
@@ -115,4 +127,11 @@ def load_data_with_foreign_keys(
115
127
 
116
128
  # Set many-to-many relationships
117
129
  for field_name, related_objs in m2m_relationships.items():
118
- getattr(obj, field_name).set(related_objs)
130
+ if related_objs: # Only set if there are objects to set
131
+ getattr(obj, field_name).set(related_objs)
132
+ if verbose:
133
+ command.stdout.write(
134
+ command.style.SUCCESS(
135
+ f"Set {len(related_objs)} {field_name} for {model.__name__} {name}"
136
+ )
137
+ )