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.
- endoreg_db/data/__init__.py +14 -0
- endoreg_db/data/disease_classification/chronic_kidney_disease.yaml +2 -2
- endoreg_db/data/disease_classification_choice/chronic_kidney_disease.yaml +6 -6
- endoreg_db/data/examination/examinations/data.yaml +22 -21
- endoreg_db/data/examination/type/data.yaml +12 -0
- endoreg_db/data/examination_indication/endoscopy.yaml +417 -1
- endoreg_db/data/examination_indication_classification/endoscopy.yaml +157 -5
- endoreg_db/data/finding/data.yaml +18 -11
- endoreg_db/data/finding_intervention/endoscopy.yaml +26 -121
- endoreg_db/data/finding_intervention/endoscopy_colonoscopy.yaml +163 -0
- endoreg_db/data/finding_intervention/endoscopy_egd.yaml +128 -0
- endoreg_db/data/finding_intervention/endoscopy_ercp.yaml +32 -0
- endoreg_db/data/finding_intervention/endoscopy_eus_lower.yaml +9 -0
- endoreg_db/data/finding_intervention/endoscopy_eus_upper.yaml +36 -0
- endoreg_db/data/information_source/endoscopy_guidelines.yaml +7 -0
- endoreg_db/data/medication_indication/anticoagulation.yaml +4 -4
- endoreg_db/data/pdf_type/data.yaml +9 -16
- endoreg_db/data/requirement/colonoscopy_indications.yaml +56 -0
- endoreg_db/data/requirement/disease_cardiovascular.yaml +79 -0
- endoreg_db/data/requirement/disease_classification_choice_cardiovascular.yaml +38 -0
- endoreg_db/data/requirement/disease_hepatology.yaml +12 -0
- endoreg_db/data/requirement/disease_misc.yaml +12 -0
- endoreg_db/data/requirement/disease_renal.yaml +80 -0
- endoreg_db/data/requirement/event_cardiology.yaml +251 -0
- endoreg_db/data/requirement/lab_value.yaml +120 -0
- endoreg_db/data/requirement_operator/lab_operators.yaml +128 -0
- endoreg_db/data/requirement_operator/model_operators.yaml +90 -0
- endoreg_db/data/requirement_set/endoscopy_bleeding_risk.yaml +12 -0
- endoreg_db/data/requirement_set_type/data.yaml +20 -0
- endoreg_db/data/requirement_type/requirement_types.yaml +83 -0
- endoreg_db/data/risk/bleeding.yaml +26 -0
- endoreg_db/data/risk/thrombosis.yaml +37 -0
- endoreg_db/data/risk_type/data.yaml +27 -0
- endoreg_db/data/unit/time.yaml +36 -1
- endoreg_db/management/commands/load_base_db_data.py +14 -1
- endoreg_db/management/commands/load_center_data.py +46 -21
- endoreg_db/management/commands/load_examination_indication_data.py +49 -27
- endoreg_db/management/commands/load_requirement_data.py +156 -0
- endoreg_db/management/commands/load_risk_data.py +56 -0
- endoreg_db/migrations/0009_requirementoperator_requirementsettype_and_more.py +154 -0
- endoreg_db/models/__init__.py +20 -0
- endoreg_db/models/ai_model/ai_model.py +0 -13
- endoreg_db/models/ai_model/model_meta.py +2 -12
- endoreg_db/models/data_file/base_classes/abstract_frame.py +0 -2
- endoreg_db/models/data_file/base_classes/abstract_pdf.py +0 -9
- endoreg_db/models/data_file/base_classes/abstract_video.py +7 -8
- endoreg_db/models/data_file/base_classes/utils.py +0 -22
- endoreg_db/models/data_file/frame.py +1 -1
- endoreg_db/models/data_file/import_classes/raw_pdf.py +5 -11
- endoreg_db/models/data_file/import_classes/raw_video.py +6 -4
- endoreg_db/models/data_file/video/video.py +3 -3
- endoreg_db/models/disease.py +88 -19
- endoreg_db/models/event.py +108 -21
- endoreg_db/models/examination/examination_indication.py +108 -29
- endoreg_db/models/examination/examination_type.py +20 -6
- endoreg_db/models/information_source.py +37 -1
- endoreg_db/models/laboratory/lab_value.py +83 -32
- endoreg_db/models/requirement/__init__.py +11 -0
- endoreg_db/models/requirement/requirement.py +325 -0
- endoreg_db/models/requirement/requirement_evaluation/__init__.py +134 -0
- endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +102 -0
- endoreg_db/models/requirement/requirement_operator.py +58 -0
- endoreg_db/models/requirement/requirement_set.py +127 -0
- endoreg_db/models/risk/__init__.py +7 -0
- endoreg_db/models/risk/risk.py +72 -0
- endoreg_db/models/risk/risk_type.py +55 -0
- endoreg_db/serializers/video_segmentation.py +12 -25
- endoreg_db/urls.py +17 -3
- endoreg_db/utils/__init__.py +43 -0
- endoreg_db/utils/dataloader.py +38 -19
- endoreg_db/utils/hashs.py +1 -0
- endoreg_db/utils/paths.py +86 -0
- endoreg_db/views/views.py +107 -0
- {endoreg_db-0.6.3.dist-info → endoreg_db-0.6.4.dist-info}/METADATA +1 -1
- {endoreg_db-0.6.3.dist-info → endoreg_db-0.6.4.dist-info}/RECORD +77 -42
- endoreg_db/management/commands/load_name_data.py +0 -37
- {endoreg_db-0.6.3.dist-info → endoreg_db-0.6.4.dist-info}/WHEEL +0 -0
- {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,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
|
-
|
|
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
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
-
#
|
|
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
|
|
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
|
-
|
|
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.
|
endoreg_db/utils/__init__.py
CHANGED
|
@@ -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
|
]
|
endoreg_db/utils/dataloader.py
CHANGED
|
@@ -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
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
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
|
+
)
|