endoreg-db 0.5.3__py3-none-any.whl → 0.6.1__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 (268) hide show
  1. endoreg_db/admin.py +90 -1
  2. endoreg_db/case_generator/case_generator.py +159 -0
  3. endoreg_db/case_generator/lab_sample_factory.py +33 -0
  4. endoreg_db/case_generator/utils.py +30 -0
  5. endoreg_db/data/__init__.py +50 -4
  6. endoreg_db/data/ai_model/data.yaml +7 -0
  7. endoreg_db/data/{label → ai_model_label}/label/data.yaml +27 -1
  8. endoreg_db/data/ai_model_label/label-set/data.yaml +21 -0
  9. endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +5 -0
  10. endoreg_db/data/ai_model_video_segmentation_label/base_segmentation.yaml +176 -0
  11. endoreg_db/data/ai_model_video_segmentation_labelset/data.yaml +20 -0
  12. endoreg_db/data/center/data.yaml +35 -5
  13. endoreg_db/data/contraindication/bleeding.yaml +11 -0
  14. endoreg_db/data/distribution/numeric/data.yaml +14 -0
  15. endoreg_db/data/endoscope/data.yaml +93 -0
  16. endoreg_db/data/examination_indication/endoscopy.yaml +8 -0
  17. endoreg_db/data/examination_indication_classification/endoscopy.yaml +8 -0
  18. endoreg_db/data/examination_indication_classification_choice/endoscopy.yaml +101 -0
  19. endoreg_db/data/finding/data.yaml +141 -0
  20. endoreg_db/data/finding_intervention/endoscopy.yaml +138 -0
  21. endoreg_db/data/finding_intervention_type/endoscopy.yaml +15 -0
  22. endoreg_db/data/finding_location_classification/colonoscopy.yaml +46 -0
  23. endoreg_db/data/finding_location_classification_choice/colonoscopy.yaml +240 -0
  24. endoreg_db/data/finding_morphology_classification/colonoscopy.yaml +48 -0
  25. endoreg_db/data/finding_morphology_classification_choice/colon_lesion_circularity_default.yaml +34 -0
  26. endoreg_db/data/finding_morphology_classification_choice/colon_lesion_nice.yaml +20 -0
  27. endoreg_db/data/finding_morphology_classification_choice/colon_lesion_paris.yaml +65 -0
  28. endoreg_db/data/finding_morphology_classification_choice/colon_lesion_planarity_default.yaml +56 -0
  29. endoreg_db/data/finding_morphology_classification_choice/colon_lesion_surface_intact_default.yaml +39 -0
  30. endoreg_db/data/finding_morphology_classification_choice/colonoscopy_size.yaml +57 -0
  31. endoreg_db/data/finding_morphology_classification_type/colonoscopy.yaml +79 -0
  32. endoreg_db/data/finding_type/data.yaml +30 -0
  33. endoreg_db/data/gender/data.yaml +17 -0
  34. endoreg_db/data/lab_value/cardiac_enzymes.yaml +7 -1
  35. endoreg_db/data/lab_value/coagulation.yaml +6 -1
  36. endoreg_db/data/lab_value/electrolytes.yaml +39 -1
  37. endoreg_db/data/lab_value/gastrointestinal_function.yaml +12 -0
  38. endoreg_db/data/lab_value/hematology.yaml +17 -2
  39. endoreg_db/data/lab_value/hormones.yaml +6 -0
  40. endoreg_db/data/lab_value/lipids.yaml +12 -3
  41. endoreg_db/data/lab_value/misc.yaml +5 -2
  42. endoreg_db/data/lab_value/renal_function.yaml +2 -1
  43. endoreg_db/data/lx_client_tag/base.yaml +54 -0
  44. endoreg_db/data/lx_client_type/base.yaml +30 -0
  45. endoreg_db/data/lx_permission/base.yaml +24 -0
  46. endoreg_db/data/lx_permission/endoreg.yaml +52 -0
  47. endoreg_db/data/medication_indication/anticoagulation.yaml +44 -49
  48. endoreg_db/data/names_first/first_names.yaml +51 -0
  49. endoreg_db/data/names_last/last_names.yaml +51 -0
  50. endoreg_db/data/network_device/data.yaml +30 -0
  51. endoreg_db/data/organ/data.yaml +29 -0
  52. endoreg_db/data/pdf_type/data.yaml +2 -1
  53. endoreg_db/data/report_reader_flag/ukw-examination-generic.yaml +4 -0
  54. endoreg_db/forms/__init__.py +3 -1
  55. endoreg_db/forms/examination_form.py +11 -0
  56. endoreg_db/forms/patient_finding_intervention_form.py +19 -0
  57. endoreg_db/forms/patient_form.py +26 -0
  58. endoreg_db/management/commands/__init__.py +0 -0
  59. endoreg_db/management/commands/load_ai_model_data.py +57 -23
  60. endoreg_db/management/commands/load_ai_model_label_data.py +59 -0
  61. endoreg_db/management/commands/load_base_db_data.py +160 -118
  62. endoreg_db/management/commands/{load_endoscope_type_data.py → load_contraindication_data.py} +3 -7
  63. endoreg_db/management/commands/load_disease_data.py +29 -7
  64. endoreg_db/management/commands/load_endoscope_data.py +68 -0
  65. endoreg_db/management/commands/load_examination_indication_data.py +65 -0
  66. endoreg_db/management/commands/load_finding_data.py +171 -0
  67. endoreg_db/management/commands/load_lab_value_data.py +3 -3
  68. endoreg_db/management/commands/load_lx_data.py +64 -0
  69. endoreg_db/management/commands/load_medication_data.py +83 -21
  70. endoreg_db/management/commands/load_name_data.py +37 -0
  71. endoreg_db/management/commands/{load_endoscopy_processor_data.py → load_organ_data.py} +7 -9
  72. endoreg_db/migrations/0001_initial.py +1206 -728
  73. endoreg_db/migrations/0002_alter_frame_image_alter_rawframe_image.py +23 -0
  74. endoreg_db/migrations/0003_alter_frame_image_alter_rawframe_image.py +23 -0
  75. endoreg_db/migrations/0004_alter_rawvideofile_file_alter_video_file.py +25 -0
  76. endoreg_db/migrations/0005_rawvideofile_frame_count_and_more.py +33 -0
  77. endoreg_db/migrations/0006_frame_extracted_rawframe_extracted.py +23 -0
  78. endoreg_db/migrations/0007_rename_pseudo_patient_video_patient_and_more.py +24 -0
  79. endoreg_db/migrations/0008_remove_reportfile_patient_examination_and_more.py +48 -0
  80. endoreg_db/models/__init__.py +331 -28
  81. endoreg_db/models/ai_model/__init__.py +1 -0
  82. endoreg_db/models/ai_model/ai_model.py +103 -0
  83. endoreg_db/models/ai_model/lightning/__init__.py +3 -0
  84. endoreg_db/models/ai_model/lightning/inference_dataset.py +53 -0
  85. endoreg_db/models/ai_model/lightning/multilabel_classification_net.py +155 -0
  86. endoreg_db/models/ai_model/lightning/postprocess.py +53 -0
  87. endoreg_db/models/ai_model/lightning/predict.py +172 -0
  88. endoreg_db/models/ai_model/lightning/prediction_visualizer.py +55 -0
  89. endoreg_db/models/ai_model/lightning/preprocess.py +68 -0
  90. endoreg_db/models/ai_model/lightning/run_visualizer.py +21 -0
  91. endoreg_db/models/ai_model/model_meta.py +232 -6
  92. endoreg_db/models/ai_model/model_type.py +13 -3
  93. endoreg_db/models/annotation/__init__.py +31 -2
  94. endoreg_db/models/annotation/anonymized_image_annotation.py +73 -18
  95. endoreg_db/models/annotation/binary_classification_annotation_task.py +94 -57
  96. endoreg_db/models/annotation/image_classification.py +73 -14
  97. endoreg_db/models/annotation/video_segmentation_annotation.py +52 -0
  98. endoreg_db/models/annotation/video_segmentation_labelset.py +20 -0
  99. endoreg_db/models/case/__init__.py +1 -0
  100. endoreg_db/models/{persons/patient/case → case}/case.py +4 -0
  101. endoreg_db/models/case_template/__init__.py +10 -1
  102. endoreg_db/models/case_template/case_template.py +57 -13
  103. endoreg_db/models/case_template/case_template_rule.py +5 -5
  104. endoreg_db/models/case_template/case_template_rule_value.py +19 -4
  105. endoreg_db/models/center/__init__.py +7 -0
  106. endoreg_db/models/center/center.py +31 -5
  107. endoreg_db/models/center/center_product.py +0 -1
  108. endoreg_db/models/center/center_resource.py +16 -2
  109. endoreg_db/models/center/center_waste.py +6 -1
  110. endoreg_db/models/contraindication/__init__.py +21 -0
  111. endoreg_db/models/data_file/__init__.py +38 -5
  112. endoreg_db/models/data_file/base_classes/__init__.py +6 -1
  113. endoreg_db/models/data_file/base_classes/abstract_frame.py +64 -15
  114. endoreg_db/models/data_file/base_classes/abstract_pdf.py +136 -0
  115. endoreg_db/models/data_file/base_classes/abstract_video.py +744 -138
  116. endoreg_db/models/data_file/base_classes/frame_helpers.py +17 -0
  117. endoreg_db/models/data_file/base_classes/prepare_bulk_frames.py +19 -0
  118. endoreg_db/models/data_file/base_classes/utils.py +80 -0
  119. endoreg_db/models/data_file/frame.py +22 -38
  120. endoreg_db/models/data_file/import_classes/__init__.py +4 -18
  121. endoreg_db/models/data_file/import_classes/raw_pdf.py +162 -90
  122. endoreg_db/models/data_file/import_classes/raw_video.py +239 -294
  123. endoreg_db/models/data_file/metadata/__init__.py +10 -0
  124. endoreg_db/models/data_file/metadata/pdf_meta.py +4 -0
  125. endoreg_db/models/data_file/metadata/sensitive_meta.py +265 -6
  126. endoreg_db/models/data_file/metadata/video_meta.py +116 -50
  127. endoreg_db/models/data_file/report_file.py +30 -63
  128. endoreg_db/models/data_file/video/__init__.py +6 -2
  129. endoreg_db/models/data_file/video/video.py +187 -16
  130. endoreg_db/models/data_file/video_segment.py +162 -55
  131. endoreg_db/models/disease.py +25 -2
  132. endoreg_db/models/emission/__init__.py +5 -1
  133. endoreg_db/models/emission/emission_factor.py +71 -6
  134. endoreg_db/models/event.py +51 -0
  135. endoreg_db/models/examination/__init__.py +6 -1
  136. endoreg_db/models/examination/examination.py +53 -12
  137. endoreg_db/models/examination/examination_indication.py +170 -0
  138. endoreg_db/models/examination/examination_time.py +31 -5
  139. endoreg_db/models/examination/examination_time_type.py +28 -4
  140. endoreg_db/models/examination/examination_type.py +28 -6
  141. endoreg_db/models/finding/__init__.py +11 -0
  142. endoreg_db/models/finding/finding.py +75 -0
  143. endoreg_db/models/finding/finding_intervention.py +60 -0
  144. endoreg_db/models/finding/finding_location_classification.py +94 -0
  145. endoreg_db/models/finding/finding_morphology_classification.py +89 -0
  146. endoreg_db/models/finding/finding_type.py +22 -0
  147. endoreg_db/models/hardware/endoscope.py +16 -0
  148. endoreg_db/models/hardware/endoscopy_processor.py +31 -19
  149. endoreg_db/models/label/label.py +35 -7
  150. endoreg_db/models/laboratory/lab_value.py +12 -3
  151. endoreg_db/models/logging/__init__.py +8 -1
  152. endoreg_db/models/lx/__init__.py +4 -0
  153. endoreg_db/models/lx/client.py +57 -0
  154. endoreg_db/models/lx/identity.py +34 -0
  155. endoreg_db/models/lx/permission.py +18 -0
  156. endoreg_db/models/lx/user.py +16 -0
  157. endoreg_db/models/medication/__init__.py +19 -1
  158. endoreg_db/models/medication/medication.py +7 -122
  159. endoreg_db/models/medication/medication_indication.py +50 -0
  160. endoreg_db/models/medication/medication_indication_type.py +34 -0
  161. endoreg_db/models/medication/medication_intake_time.py +26 -0
  162. endoreg_db/models/medication/medication_schedule.py +37 -0
  163. endoreg_db/models/network/__init__.py +7 -1
  164. endoreg_db/models/network/network_device.py +13 -8
  165. endoreg_db/models/organ/__init__.py +38 -0
  166. endoreg_db/models/other/__init__.py +19 -1
  167. endoreg_db/models/other/distribution/__init__.py +44 -0
  168. endoreg_db/models/other/distribution/base_value_distribution.py +20 -0
  169. endoreg_db/models/other/distribution/date_value_distribution.py +91 -0
  170. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +32 -0
  171. endoreg_db/models/other/distribution/numeric_value_distribution.py +97 -0
  172. endoreg_db/models/other/distribution/single_categorical_value_distribution.py +22 -0
  173. endoreg_db/models/other/distribution.py +1 -211
  174. endoreg_db/models/other/material.py +4 -0
  175. endoreg_db/models/other/transport_route.py +2 -1
  176. endoreg_db/models/patient/__init__.py +24 -0
  177. endoreg_db/models/patient/patient_examination.py +182 -0
  178. endoreg_db/models/patient/patient_finding.py +143 -0
  179. endoreg_db/models/patient/patient_finding_intervention.py +26 -0
  180. endoreg_db/models/patient/patient_finding_location.py +120 -0
  181. endoreg_db/models/patient/patient_finding_morphology.py +166 -0
  182. endoreg_db/models/persons/__init__.py +29 -2
  183. endoreg_db/models/persons/examiner/examiner.py +48 -4
  184. endoreg_db/models/persons/patient/__init__.py +1 -1
  185. endoreg_db/models/persons/patient/patient.py +227 -54
  186. endoreg_db/models/persons/patient/patient_disease.py +6 -0
  187. endoreg_db/models/persons/patient/patient_event.py +31 -1
  188. endoreg_db/models/persons/patient/patient_examination_indication.py +32 -0
  189. endoreg_db/models/persons/patient/patient_lab_sample.py +4 -2
  190. endoreg_db/models/persons/patient/patient_lab_value.py +37 -16
  191. endoreg_db/models/persons/patient/patient_medication.py +27 -12
  192. endoreg_db/models/persons/patient/patient_medication_schedule.py +62 -2
  193. endoreg_db/models/prediction/__init__.py +7 -1
  194. endoreg_db/models/prediction/image_classification.py +20 -6
  195. endoreg_db/models/prediction/video_prediction_meta.py +151 -89
  196. endoreg_db/models/product/__init__.py +10 -1
  197. endoreg_db/models/product/product.py +15 -2
  198. endoreg_db/models/product/product_group.py +8 -0
  199. endoreg_db/models/product/product_material.py +4 -0
  200. endoreg_db/models/product/product_weight.py +12 -0
  201. endoreg_db/models/product/reference_product.py +19 -3
  202. endoreg_db/models/quiz/__init__.py +8 -1
  203. endoreg_db/models/report_reader/__init__.py +6 -1
  204. endoreg_db/serializers/__init__.py +1 -1
  205. endoreg_db/serializers/annotation.py +2 -5
  206. endoreg_db/serializers/frame.py +1 -5
  207. endoreg_db/serializers/patient.py +26 -3
  208. endoreg_db/serializers/prediction.py +2 -7
  209. endoreg_db/serializers/raw_video_meta_validation.py +13 -0
  210. endoreg_db/serializers/video.py +6 -13
  211. endoreg_db/serializers/video_segmentation.py +492 -0
  212. endoreg_db/templates/admin/patient_finding_intervention.html +253 -0
  213. endoreg_db/templates/admin/start_examination.html +12 -0
  214. endoreg_db/templates/timeline.html +176 -0
  215. endoreg_db/urls.py +173 -0
  216. endoreg_db/utils/__init__.py +36 -1
  217. endoreg_db/utils/dataloader.py +45 -19
  218. endoreg_db/utils/dates.py +39 -0
  219. endoreg_db/utils/hashs.py +122 -4
  220. endoreg_db/utils/names.py +74 -0
  221. endoreg_db/utils/parse_and_generate_yaml.py +46 -0
  222. endoreg_db/utils/pydantic_models/__init__.py +6 -0
  223. endoreg_db/utils/pydantic_models/db_config.py +57 -0
  224. endoreg_db/utils/validate_endo_roi.py +19 -0
  225. endoreg_db/utils/validate_subcategory_dict.py +91 -0
  226. endoreg_db/utils/video/__init__.py +13 -0
  227. endoreg_db/utils/video/extract_frames.py +121 -0
  228. endoreg_db/utils/video/transcode_videofile.py +111 -0
  229. endoreg_db/views/__init__.py +2 -0
  230. endoreg_db/views/csrf.py +7 -0
  231. endoreg_db/views/patient_views.py +90 -0
  232. endoreg_db/views/raw_video_meta_validation_views.py +38 -0
  233. endoreg_db/views/report_views.py +96 -0
  234. endoreg_db/views/video_segmentation_views.py +149 -0
  235. endoreg_db/views/views_for_timeline.py +46 -0
  236. endoreg_db/views.py +0 -3
  237. endoreg_db-0.6.1.dist-info/METADATA +151 -0
  238. endoreg_db-0.6.1.dist-info/RECORD +420 -0
  239. {endoreg_db-0.5.3.dist-info → endoreg_db-0.6.1.dist-info}/WHEEL +1 -1
  240. endoreg_db/data/active_model/data.yaml +0 -3
  241. endoreg_db/data/label/label-set/data.yaml +0 -18
  242. endoreg_db/management/commands/delete_legacy_images.py +0 -19
  243. endoreg_db/management/commands/delete_legacy_videos.py +0 -17
  244. endoreg_db/management/commands/extract_legacy_video_frames.py +0 -18
  245. endoreg_db/management/commands/import_legacy_images.py +0 -94
  246. endoreg_db/management/commands/import_legacy_videos.py +0 -76
  247. endoreg_db/management/commands/load_label_data.py +0 -67
  248. endoreg_db/migrations/0002_anonymizedimagelabel_anonymousimageannotation_and_more.py +0 -55
  249. endoreg_db/migrations/0003_anonymousimageannotation_original_image_url_and_more.py +0 -39
  250. endoreg_db/migrations/0004_alter_rawpdffile_file.py +0 -20
  251. endoreg_db/migrations/0005_uploadedfile_alter_rawpdffile_file_anonymizedfile.py +0 -40
  252. endoreg_db/migrations/0006_alter_rawpdffile_file.py +0 -20
  253. endoreg_db/migrations/0007_networkdevicelogentry_datetime_and_more.py +0 -43
  254. endoreg_db/migrations/0008_networkdevicelogentry_aglnet_ip_and_more.py +0 -28
  255. endoreg_db/migrations/0009_alter_networkdevicelogentry_vpn_service_status.py +0 -18
  256. endoreg_db/migrations/0010_remove_networkdevicelogentry_hostname.py +0 -17
  257. endoreg_db/models/legacy_data/__init__.py +0 -3
  258. endoreg_db/models/legacy_data/image.py +0 -34
  259. endoreg_db/models/patient_examination/__init__.py +0 -35
  260. endoreg_db/utils/video_metadata.py +0 -87
  261. endoreg_db-0.5.3.dist-info/METADATA +0 -28
  262. endoreg_db-0.5.3.dist-info/RECORD +0 -319
  263. /endoreg_db/{models/persons/patient/case → case_generator}/__init__.py +0 -0
  264. /endoreg_db/data/{label → ai_model_label}/label-type/data.yaml +0 -0
  265. /endoreg_db/data/{model_type → ai_model_type}/data.yaml +0 -0
  266. /endoreg_db/{data/distribution/numeric/.init → management/__init__.py} +0 -0
  267. /endoreg_db/management/commands/{load_report_reader_flag.py → load_report_reader_flag_data.py} +0 -0
  268. {endoreg_db-0.5.3.dist-info → endoreg_db-0.6.1.dist-info}/licenses/LICENSE +0 -0
@@ -2,10 +2,13 @@ import os
2
2
  import yaml
3
3
  from django.core.exceptions import ObjectDoesNotExist
4
4
 
5
+ from icecream import ic
6
+
7
+
5
8
  def load_model_data_from_yaml(command, model_name, metadata, verbose):
6
9
  """
7
10
  Load model data from YAML files.
8
-
11
+
9
12
  Args:
10
13
  command: Command object for stdout writing.
11
14
  model_name: Name of the model being loaded.
@@ -19,19 +22,24 @@ def load_model_data_from_yaml(command, model_name, metadata, verbose):
19
22
  foreign_keys = metadata["foreign_keys"]
20
23
  foreign_key_models = metadata["foreign_key_models"]
21
24
 
22
- _files = [f for f in os.listdir(dir_path) if f.endswith('.yaml')]
25
+ _files = [f for f in os.listdir(dir_path) if f.endswith(".yaml")]
23
26
  # sort
24
27
  _files.sort()
25
28
  for file in _files:
26
- with open(os.path.join(dir_path, file), 'r') as file:
29
+ with open(os.path.join(dir_path, file), "r", encoding="utf-8") as file:
27
30
  yaml_data = yaml.safe_load(file)
28
-
29
- load_data_with_foreign_keys(command, model, yaml_data, foreign_keys, foreign_key_models, verbose)
30
31
 
31
- def load_data_with_foreign_keys(command, model, yaml_data, foreign_keys, foreign_key_models, verbose):
32
+ load_data_with_foreign_keys(
33
+ command, model, yaml_data, foreign_keys, foreign_key_models, verbose
34
+ )
35
+
36
+
37
+ def load_data_with_foreign_keys(
38
+ command, model, yaml_data, foreign_keys, foreign_key_models, verbose
39
+ ):
32
40
  """
33
41
  Load data handling foreign keys and many-to-many relationships.
34
-
42
+
35
43
  Args:
36
44
  command: Command object for stdout writing.
37
45
  model: The Django model for the data.
@@ -41,20 +49,24 @@ def load_data_with_foreign_keys(command, model, yaml_data, foreign_keys, foreign
41
49
  verbose: Boolean indicating whether to print verbose output.
42
50
  """
43
51
  for entry in yaml_data:
44
- fields = entry.get('fields', {})
45
- name = fields.pop('name', None)
52
+ fields = entry.get("fields", {})
53
+ name = fields.pop("name", None)
46
54
  m2m_relationships = {} # Store many-to-many relationships
47
- print(entry)
55
+ # print(entry)
48
56
 
49
57
  # Handle foreign keys and many-to-many relationships
50
58
  for fk_field, fk_model in zip(foreign_keys, foreign_key_models):
51
- print(fk_field, fk_model)
59
+ # print(fk_field, fk_model)
52
60
  target_keys = fields.pop(fk_field, None)
53
-
61
+
54
62
  # Ensure the foreign key exists
55
63
  if target_keys is None:
56
64
  if verbose:
57
- command.stdout.write(command.style.WARNING(f"Foreign key {fk_field} not found in fields"))
65
+ command.stdout.write(
66
+ command.style.WARNING(
67
+ f"Foreign key {fk_field} not found in fields"
68
+ )
69
+ )
58
70
  continue # Skip if no foreign key provided
59
71
 
60
72
  # Process many-to-many fields or foreign keys
@@ -63,29 +75,43 @@ def load_data_with_foreign_keys(command, model, yaml_data, foreign_keys, foreign
63
75
  for key in target_keys:
64
76
  obj, created = fk_model.objects.get_or_create(name=key)
65
77
  if created and verbose:
66
- command.stdout.write(command.style.SUCCESS(f"Created {fk_model.__name__} {key}"))
78
+ command.stdout.write(
79
+ command.style.SUCCESS(f"Created {fk_model.__name__} {key}")
80
+ )
67
81
  related_objects.append(obj)
68
82
  m2m_relationships[fk_field] = related_objects
69
83
  else: # Single foreign key relationship
70
84
  try:
71
85
  if model == "endoreg_db.case_template_rule":
72
- print(fk_model, target_keys)
86
+ # print(fk_model, target_keys)
87
+ pass
73
88
  obj = fk_model.objects.get_by_natural_key(target_keys)
74
89
  except ObjectDoesNotExist:
75
90
  if verbose:
76
- command.stdout.write(command.style.WARNING(f"{fk_model.__name__} with key {target_keys} not found"))
91
+ command.stdout.write(
92
+ command.style.WARNING(
93
+ f"{fk_model.__name__} with key {target_keys} not found"
94
+ )
95
+ )
77
96
  continue
78
97
  fields[fk_field] = obj
79
-
98
+
80
99
  # Create or update the main object
81
100
  if name is None:
82
101
  obj, created = model.objects.get_or_create(**fields)
83
102
  else:
84
103
  obj, created = model.objects.update_or_create(defaults=fields, name=name)
85
104
  if created and verbose:
86
- command.stdout.write(command.style.SUCCESS(f'Created {model.__name__} {name}'))
105
+ command.stdout.write(
106
+ command.style.SUCCESS(f"Created {model.__name__} {name}")
107
+ )
87
108
  elif verbose:
88
- command.stdout.write(command.style.WARNING(f'Skipped {model.__name__} {name}, already exists'))
109
+ pass
110
+ # command.stdout.write(
111
+ # command.style.WARNING(
112
+ # f"Skipped {model.__name__} {name}, already exists"
113
+ # )
114
+ # )
89
115
 
90
116
  # Set many-to-many relationships
91
117
  for field_name, related_objs in m2m_relationships.items():
@@ -0,0 +1,39 @@
1
+ from datetime import datetime, date, timedelta
2
+ from random import randint
3
+ from calendar import monthrange
4
+
5
+ # TODO replace used random_day_by_year function implementation when
6
+ # creating pseudo patients with new function "random date by age_at_date and examination_date"
7
+
8
+
9
+ def random_day_by_age_at_date(age_at_date: int, examination_date: date) -> date:
10
+ """
11
+ Return a random birth day for a patient with the specified age at the specified examination date.
12
+ """
13
+ examination_year = examination_date.year
14
+ latest_birthdate = examination_date.replace(year=examination_year - age_at_date)
15
+ valid_dates = [latest_birthdate - timedelta(days=i) for i in range(365)]
16
+
17
+ select = randint(0, len(valid_dates) - 1)
18
+ birth_date = valid_dates[select]
19
+
20
+ return birth_date
21
+
22
+
23
+ def random_day_by_year(year: int) -> date:
24
+ """
25
+ Return a random birth day within the specified year.
26
+ """
27
+ month = randint(1, 12)
28
+ day = randint(1, monthrange(year, month)[1])
29
+
30
+ return date(year, month, day)
31
+
32
+
33
+ def random_day_by_month_year(month: int, year) -> date:
34
+ """
35
+ Return a random birth day within the specified month and year.
36
+ """
37
+
38
+ day = randint(1, monthrange(year, month)[1])
39
+ return date(year, month, day)
endoreg_db/utils/hashs.py CHANGED
@@ -1,12 +1,18 @@
1
1
  import hashlib
2
2
  from pathlib import Path
3
+ from datetime import datetime, date
4
+
5
+ import os
6
+
7
+ SALT = os.getenv("DJANGO_SALT", "default_salt")
8
+
3
9
 
4
10
  def get_video_hash(video_path):
5
11
  """
6
12
  Get the hash of a video file.
7
13
  """
8
14
  # Open the video file in read-binary mode:
9
- with open(video_path, 'rb') as f:
15
+ with open(video_path, "rb") as f:
10
16
  # Create the hash object, passing in the video contents for hashing:
11
17
  hash_object = hashlib.sha256(f.read())
12
18
  # Get the hexadecimal representation of the hash
@@ -15,14 +21,15 @@ def get_video_hash(video_path):
15
21
 
16
22
  return video_hash
17
23
 
18
- def get_pdf_hash(pdf_path:Path):
24
+
25
+ def get_pdf_hash(pdf_path: Path):
19
26
  """
20
27
  Get the hash of a pdf file.
21
28
  """
22
29
  pdf_hash = None
23
30
 
24
31
  # Open the file in binary mode and read its contents
25
- with open(pdf_path, 'rb') as f:
32
+ with open(pdf_path, "rb") as f:
26
33
  pdf_contents = f.read()
27
34
  # Create a hash object using SHA-256 algorithm
28
35
 
@@ -31,4 +38,115 @@ def get_pdf_hash(pdf_path:Path):
31
38
  pdf_hash = hash_object.hexdigest()
32
39
  assert len(pdf_hash) <= 255, "Hash length exceeds 255 characters"
33
40
 
34
- return pdf_hash
41
+ return pdf_hash
42
+
43
+
44
+ def _get_date_hash_string(date_obj: date) -> str:
45
+ # if date is datetime object, convert to date
46
+ if isinstance(date_obj, datetime):
47
+ # warnings.warn("Date is a datetime object. Converting to date object.")
48
+ date_obj = date_obj.date()
49
+ elif isinstance(date_obj, str):
50
+ # warnings.warn(f"Date is a string ({date_obj}). Converting to date object.")
51
+ date_obj = datetime.strptime(date_obj, "%Y-%m-%d").date()
52
+
53
+ assert isinstance(date_obj, date), "Date must be a date object"
54
+ # if date is 1900-01-01, make it an empty string
55
+ if date_obj == date(1900, 1, 1):
56
+ date_str = ""
57
+ else:
58
+ date_str = date_obj.strftime("%Y-%m-%d")
59
+
60
+ return date_str
61
+
62
+
63
+ def get_hash_string(
64
+ first_name: str = "",
65
+ last_name: str = "",
66
+ dob: date = date(1900, 1, 1),
67
+ center_name: str = "",
68
+ examination_date: date = date(1900, 1, 1),
69
+ endoscope_sn: str = "",
70
+ salt: str = "",
71
+ ):
72
+ """
73
+ Get the string to be hashed for a patient's first name, last name, date of birth, examination date, and endoscope serial number.
74
+ """
75
+ if not salt:
76
+ salt = SALT
77
+
78
+ examination_date_str = _get_date_hash_string(examination_date)
79
+ dob_str = _get_date_hash_string(dob)
80
+
81
+ # Concatenate the patient's first name, last name, date of birth, examination date, endoscope serial number, and salt:
82
+ hash_str = f"{first_name}{last_name}{dob_str}{center_name}{dob_str}{examination_date_str}{endoscope_sn}{salt}"
83
+ return hash_str
84
+
85
+
86
+ def get_patient_hash(
87
+ first_name: str, last_name: str, dob: date, center: str, salt: str = ""
88
+ ):
89
+ """
90
+ Get the hash of a patient's first name, last name, and date of birth.
91
+ """
92
+ # Concatenate the patient's first name, last name, date of birth, and salt:
93
+ hash_str = get_hash_string(
94
+ first_name=first_name,
95
+ last_name=last_name,
96
+ dob=dob,
97
+ center_name=center,
98
+ salt=salt,
99
+ )
100
+ # Create a hash object using SHA-256 algorithm
101
+ hash_object = hashlib.sha256(hash_str.encode())
102
+ # Get the hexadecimal representation of the hash
103
+ patient_hash = hash_object.hexdigest()
104
+
105
+ return patient_hash
106
+
107
+
108
+ def get_patient_examination_hash(
109
+ first_name: str,
110
+ last_name: str,
111
+ dob: date,
112
+ center: str,
113
+ examination_date: date,
114
+ salt: str = "",
115
+ ):
116
+ """
117
+ Get the hash of a patient's first name, last name, date of birth, and examination date.
118
+ """
119
+ # Concatenate the patient's first name, last name, date of birth, examination date, and salt:
120
+ hash_str = get_hash_string(
121
+ first_name=first_name,
122
+ last_name=last_name,
123
+ center_name=center,
124
+ dob=dob,
125
+ examination_date=examination_date,
126
+ salt=salt,
127
+ )
128
+ # Create a hash object using SHA-256 algorithm
129
+ hash_object = hashlib.sha256(hash_str.encode())
130
+ # Get the hexadecimal representation of the hash
131
+ patient_examination_hash = hash_object.hexdigest()
132
+
133
+ return patient_examination_hash
134
+
135
+
136
+ def get_examiner_hash(first_name, last_name, center_name, salt):
137
+ """
138
+ Get the hash of an examiner's first name, last name, and center name.
139
+ """
140
+ # Concatenate the examiner's first name, last name, center name, and salt:
141
+ hash_str = get_hash_string(
142
+ first_name=first_name,
143
+ last_name=last_name,
144
+ center_name=center_name,
145
+ salt=salt,
146
+ )
147
+ # Create a hash object using SHA-256 algorithm
148
+ hash_object = hashlib.sha256(hash_str.encode())
149
+ # Get the hexadecimal representation of the hash
150
+ examiner_hash = hash_object.hexdigest()
151
+
152
+ return examiner_hash
@@ -0,0 +1,74 @@
1
+ # Use faker library to generate fake names by gender
2
+ # Use german names by default
3
+
4
+ from faker import Faker
5
+ import gender_guesser.detector as gender_detector
6
+
7
+
8
+ def create_mock_examiner_name() -> tuple[str, str]:
9
+ """
10
+ Generate a mock examiner's name using the Faker library.
11
+ This function creates a tuple with a first name and a last name for a mock examiner. It utilizes the "de_DE" locale for generating German names.
12
+ Returns:
13
+ tuple[str, str]: A tuple containing the first name and the last name.
14
+ """
15
+
16
+ fake = Faker("de_DE")
17
+ first_name = fake.first_name()
18
+ last_name = fake.last_name()
19
+ return first_name, last_name
20
+
21
+
22
+ def create_mock_patient_name(gender: str) -> tuple[str, str]:
23
+ """
24
+ Generate a mock patient's name based on the provided gender using the Faker library.
25
+ This function creates a tuple with a first name and a last name for a mock patient. It utilizes the "de_DE" locale for generating German names. When the input gender string is checked:
26
+ - If it contains "male", a male name is generated.
27
+ - If it contains "female", a female name is generated.
28
+ - Otherwise, a generic name is generated without considering gender.
29
+ Parameters:
30
+ gender (str): A string indicating the gender to be used for generating the name.
31
+ Returns:
32
+ tuple[str, str]: A tuple containing the first name and the last name.
33
+ """
34
+
35
+ fake = Faker("de_DE")
36
+
37
+ if "male" in gender.lower():
38
+ gender = "male"
39
+ elif "female" in gender.lower():
40
+ gender = "female"
41
+
42
+ if gender == "male":
43
+ first_name = fake.first_name_male()
44
+ last_name = fake.last_name_male()
45
+
46
+ elif gender == "female":
47
+ first_name = fake.first_name_female()
48
+ last_name = fake.last_name_female()
49
+
50
+ else:
51
+ first_name = fake.first_name()
52
+ last_name = fake.last_name()
53
+
54
+ return first_name, last_name
55
+
56
+
57
+ def guess_name_gender(name: str) -> str:
58
+ """
59
+ Guesses the gender for a given name by using a gender detector and retrieving the corresponding Gender model instance.
60
+ Parameters:
61
+ name (str): The name for which the gender is to be determined.
62
+ Returns:
63
+ Gender: The Gender object corresponding to the detected gender name.
64
+ Raises:
65
+ Gender.DoesNotExist: If no Gender object matching the detected gender is found.
66
+ Exception: For any other exceptions that occur during gender detection or database lookup.
67
+ """
68
+
69
+ from endoreg_db.models import Gender
70
+
71
+ detector = gender_detector.Detector(case_sensitive=False)
72
+ gender_name = detector.get_gender(name)
73
+ gender = Gender.objects.get(name=gender_name)
74
+ return gender
@@ -0,0 +1,46 @@
1
+ import yaml
2
+ from pathlib import Path
3
+
4
+ # get this files path
5
+ file_path = Path(__file__)
6
+ module_root = file_path.parent.parent
7
+ data_dir = module_root / "data"
8
+
9
+
10
+ def collect_center_names(
11
+ ):
12
+ input_file_path = data_dir / "center/data.yaml"
13
+ fist_name_dir = data_dir / "names_first"
14
+ last_name_dir = data_dir / "names_last"
15
+ # Load the input YAML file
16
+ with open(input_file_path, 'r', encoding='utf-8') as file:
17
+ data = yaml.safe_load(file)
18
+
19
+ # Containers for first and last names
20
+ first_names_set = set()
21
+ last_names_set = set()
22
+
23
+ # Extract first and last names from the YAML data
24
+ for entry in data:
25
+ fields = entry.get('fields', {})
26
+ first_names_set.update(fields.get('first_names', []))
27
+ last_names_set.update(fields.get('last_names', []))
28
+
29
+ # Create a list of dictionaries for first and last names
30
+ first_names_data = [
31
+ {"model": "endoreg_db.first_name", "fields": {"name": name}}
32
+ for name in sorted(first_names_set)
33
+ ]
34
+ last_names_data = [
35
+ {"model": "endoreg_db.last_name", "fields": {"name": name}}
36
+ for name in sorted(last_names_set)
37
+ ]
38
+
39
+ # Write the data to separate YAML files
40
+ with open(fist_name_dir/"first_names.yaml", "w", encoding='utf-8') as first_file:
41
+ yaml.dump(first_names_data, first_file, allow_unicode=True, sort_keys=False)
42
+
43
+ with open(last_name_dir/"last_names.yaml", "w", encoding='utf-8') as last_file:
44
+ yaml.dump(last_names_data, last_file, allow_unicode=True, sort_keys=False)
45
+
46
+ # print("Generated first_names.yaml and last_names.yaml successfully.")
@@ -0,0 +1,6 @@
1
+ '''Module for Pydantic models.'''
2
+ from .db_config import DbConfig
3
+
4
+ __all__ = [
5
+ 'DbConfig'
6
+ ]
@@ -0,0 +1,57 @@
1
+ from pathlib import Path
2
+ from typing import Optional, Union
3
+ import yaml
4
+ from icecream import ic
5
+ from pydantic import BaseModel
6
+
7
+
8
+ class DbConfig(BaseModel):
9
+ """Database configuration model."""
10
+
11
+ engine: str = "django.db.backends.postgresql"
12
+ host: str
13
+ port: int
14
+ user: str
15
+ password: Optional[str] = None
16
+ password_file: str = "/etc/secrets/vault/SCRT_local_password_maintenance_password"
17
+ name: str
18
+
19
+ # class options should allow for unused excess data
20
+
21
+ @classmethod
22
+ def from_file(cls, path: Union[Path, str]) -> "DbConfig":
23
+ """Create a DbConfig instance from a YAML configuration file."""
24
+ if isinstance(path, str):
25
+ filepath = Path(path)
26
+ else:
27
+ filepath = path
28
+ assert filepath.exists(), f"Missing Config {filepath}"
29
+
30
+ with open(filepath, "r", encoding="utf-8") as f:
31
+ cfg = yaml.safe_load(f)
32
+
33
+ obj = cls(**cfg)
34
+
35
+ return obj
36
+
37
+ def sync_password(self):
38
+ """Load the password from the configured password file."""
39
+ with open(self.password_file, "r", encoding="utf-8") as f:
40
+ self.password = f.read().strip()
41
+
42
+ def validate(self):
43
+ """Fully validate all database configuration fields."""
44
+ self.sync_password()
45
+
46
+ assert self.host, "Missing Host"
47
+ assert self.port, "Missing Port"
48
+ assert self.user, "Missing User"
49
+ assert self.password, "Missing Password"
50
+ assert self.name, "Missing Database"
51
+
52
+ def to_file(self, target: str = "./conf/db.yml", ask_override: bool = True):
53
+ """Export the configuration to a YAML file."""
54
+ ic(target)
55
+
56
+ with open(target, "w") as f:
57
+ yaml.safe_dump(self.model_dump(), f)
@@ -0,0 +1,19 @@
1
+ def validate_endo_roi(endo_roi_dict:dict):
2
+ """
3
+ Validate endoscope ROI dictionary. The dictionary must have the following
4
+ keys: x, y, width, height. The values must be greater than or equal to 0
5
+ for x and y, and greater than 0 for width and height.
6
+ """
7
+ for key, value in endo_roi_dict.items():
8
+ if key == "x":
9
+ assert value >= 0, f"Endoscope ROI x value must be greater than or equal to 0. Got {value}"
10
+ elif key == "y":
11
+ assert value >= 0, f"Endoscope ROI y value must be greater than or equal to 0. Got {value}"
12
+ elif key == "width":
13
+ assert value > 0, f"Endoscope ROI width value must be greater than 0. Got {value}"
14
+ elif key == "height":
15
+ assert value > 0, f"Endoscope ROI height value must be greater than 0. Got {value}"
16
+ else:
17
+ raise ValueError(f"Endoscope ROI key {key} not recognized")
18
+
19
+ return True
@@ -0,0 +1,91 @@
1
+ from endoreg_db.models import Unit
2
+
3
+ def validate_subcategory_dict(self, subcategory_dict:dict=None):
4
+ if subcategory_dict is None:
5
+ return False
6
+
7
+ if not isinstance(subcategory_dict, dict):
8
+ return False
9
+
10
+ # check if key choices exists and is a list of strings
11
+ if "choices" not in subcategory_dict:
12
+ return False
13
+
14
+ if not isinstance(subcategory_dict["choices"], list):
15
+ return False
16
+
17
+ for choice in subcategory_dict["choices"]:
18
+ if not isinstance(choice, str):
19
+ return False
20
+
21
+ # check if key default exists and is a string
22
+ if "default" not in subcategory_dict:
23
+ return False
24
+
25
+ if not isinstance(subcategory_dict["default"], str):
26
+ return False
27
+
28
+ # check if default is in choices
29
+ if subcategory_dict["default"] not in subcategory_dict["choices"]:
30
+ return False
31
+
32
+ if not subcategory_dict["required"]:
33
+ return False
34
+
35
+ return True
36
+
37
+
38
+ def validate_numerical_descriptor(self, numerical_descriptor_dict:dict=None):
39
+ # Check if numerical_descriptor_dict is None
40
+ error_message = ""
41
+ if numerical_descriptor_dict is None:
42
+ error_message = "numerical_descriptor_dict is None"
43
+ return False, error_message
44
+
45
+ # Check if numerical_descriptor_dict is a dictionary
46
+ if not isinstance(numerical_descriptor_dict, dict):
47
+ error_message = "numerical_descriptor_dict is not a dictionary"
48
+ return False, error_message
49
+
50
+ # if key unit exists and is a string and Unit object with that name exists
51
+ if "unit" not in numerical_descriptor_dict:
52
+ error_message = "unit key does not exist in numerical_descriptor_dict"
53
+ return False, error_message
54
+
55
+ elif not isinstance(numerical_descriptor_dict["unit"], str):
56
+ error_message = "unit key is not a string"
57
+ return False, error_message
58
+
59
+ elif not Unit.objects.filter(name=numerical_descriptor_dict["unit"]).exists():
60
+ error_message = "Unit object with that name does not exist"
61
+ return False, error_message
62
+
63
+
64
+ if not "required" in numerical_descriptor_dict:
65
+ error_message = "required key does not exist in numerical_descriptor_dict"
66
+ return False, error_message
67
+
68
+ elif not isinstance(numerical_descriptor_dict["required"], bool):
69
+ error_message = "required key is not a boolean"
70
+ return False, error_message
71
+
72
+ # check if min, max, mean, std exist and are either None or float
73
+ for key in ["min", "max", "mean", "std", "default"]:
74
+ if key not in numerical_descriptor_dict:
75
+ error_message = f"{key} key does not exist in numerical_descriptor_dict"
76
+ return False, error_message
77
+
78
+ if numerical_descriptor_dict[key] is not None and not isinstance(numerical_descriptor_dict[key], float):
79
+ error_message = f"{key} key is not a float"
80
+ return False, error_message
81
+
82
+ # check if distribution exists and is either "normal" or "uniform"
83
+ if "distribution" not in numerical_descriptor_dict:
84
+ error_message = "distribution key does not exist in numerical_descriptor_dict"
85
+ return False, error_message
86
+
87
+ if numerical_descriptor_dict["distribution"] not in ["normal", "uniform"]:
88
+ error_message = "distribution key is not either 'normal' or 'uniform'"
89
+ return False, error_message
90
+
91
+ return True, None
@@ -0,0 +1,13 @@
1
+ from .transcode_videofile import (
2
+ transcode_videofile,
3
+ transcode_videofile_if_required,
4
+ )
5
+
6
+ from .extract_frames import extract_frames, initialize_frame_objects
7
+
8
+ __all__ = [
9
+ "extract_frames",
10
+ "initialize_frame_objects",
11
+ "transcode_videofile",
12
+ "transcode_videofile_if_required",
13
+ ]