endoreg-db 0.3.6__py3-none-any.whl → 0.8.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 (970) hide show
  1. endoreg_db/admin.py +92 -3
  2. endoreg_db/api_urls.py +4 -0
  3. endoreg_db/apps.py +18 -6
  4. endoreg_db/assets/dummy_model.ckpt +1 -0
  5. endoreg_db/codemods/readme.md +88 -0
  6. endoreg_db/codemods/rename_datetime_fields.py +92 -0
  7. endoreg_db/config/env.py +101 -0
  8. endoreg_db/data/__init__.py +144 -65
  9. endoreg_db/data/ai_model/data.yaml +7 -0
  10. endoreg_db/data/{label → ai_model_label}/label/data.yaml +88 -62
  11. endoreg_db/data/ai_model_label/label/polyp_classification.yaml +52 -0
  12. endoreg_db/data/ai_model_label/label-set/data.yaml +40 -0
  13. endoreg_db/data/ai_model_label/label-set/polyp_classifications.yaml +25 -0
  14. endoreg_db/data/{label → ai_model_label}/label-type/data.yaml +6 -6
  15. endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +27 -0
  16. endoreg_db/data/{model_type → ai_model_type}/data.yaml +6 -6
  17. endoreg_db/data/ai_model_video_segmentation_label/base_segmentation.yaml +176 -0
  18. endoreg_db/data/ai_model_video_segmentation_labelset/data.yaml +20 -0
  19. endoreg_db/data/case_template/rule/00_patient_lab_sample_add_default_value.yaml +167 -167
  20. endoreg_db/data/case_template/rule/01_patient-set-age.yaml +7 -7
  21. endoreg_db/data/case_template/rule/01_patient-set-gender.yaml +8 -8
  22. endoreg_db/data/case_template/rule/11_create_patient_lab_sample.yaml +22 -22
  23. endoreg_db/data/case_template/rule/12_create-patient_medication-anticoagulation.yaml +18 -18
  24. endoreg_db/data/case_template/rule/13_create-patient_medication_schedule-anticoagulation.yaml +18 -18
  25. endoreg_db/data/case_template/rule/19_create_patient.yaml +16 -16
  26. endoreg_db/data/case_template/rule_type/base_types.yaml +35 -35
  27. endoreg_db/data/case_template/rule_value_type/base_types.yaml +58 -58
  28. endoreg_db/data/case_template/template/base.yaml +7 -7
  29. endoreg_db/data/case_template/template_type/pre_endoscopy.yaml +2 -2
  30. endoreg_db/data/case_template/tmp/_rule_value +13 -13
  31. endoreg_db/data/case_template/tmp/rule/01_atrial_fibrillation.yaml +21 -21
  32. endoreg_db/data/case_template/tmp/rule/02_create_object.yaml +9 -9
  33. endoreg_db/data/case_template/tmp/template/atrial_fibrillation_low_risk.yaml +6 -6
  34. endoreg_db/data/center/data.yaml +90 -59
  35. endoreg_db/data/center_resource/green_endoscopy_dashboard_CenterResource.yaml +144 -144
  36. endoreg_db/data/center_shift/ukw.yaml +9 -0
  37. endoreg_db/data/center_waste/green_endoscopy_dashboard_CenterWaste.yaml +48 -48
  38. endoreg_db/data/contraindication/bleeding.yaml +11 -0
  39. endoreg_db/data/db_summary.csv +58 -0
  40. endoreg_db/data/db_summary.xlsx +0 -0
  41. endoreg_db/data/disease/cardiovascular.yaml +37 -37
  42. endoreg_db/data/disease/hepatology.yaml +4 -4
  43. endoreg_db/data/disease/misc.yaml +5 -6
  44. endoreg_db/data/disease/renal.yaml +4 -4
  45. endoreg_db/data/disease_classification/chronic_kidney_disease.yaml +6 -6
  46. endoreg_db/data/disease_classification/coronary_vessel_disease.yaml +5 -5
  47. endoreg_db/data/disease_classification_choice/chronic_kidney_disease.yaml +41 -41
  48. endoreg_db/data/disease_classification_choice/coronary_vessel_disease.yaml +19 -19
  49. endoreg_db/data/distribution/date/patient.yaml +6 -6
  50. endoreg_db/data/distribution/numeric/data.yaml +14 -0
  51. endoreg_db/data/distribution/single_categorical/patient.yaml +6 -6
  52. endoreg_db/data/emission_factor/green_endoscopy_dashboard_EmissionFactor.yaml +132 -132
  53. endoreg_db/data/endoscope/data.yaml +93 -0
  54. endoreg_db/data/endoscope_type/data.yaml +10 -10
  55. endoreg_db/data/endoscopy_processor/data.yaml +50 -45
  56. endoreg_db/data/event/cardiology.yaml +15 -28
  57. endoreg_db/data/event/neurology.yaml +13 -13
  58. endoreg_db/data/event/surgery.yaml +12 -12
  59. endoreg_db/data/event/thrombembolism.yaml +19 -19
  60. endoreg_db/data/examination/examinations/data.yaml +72 -66
  61. endoreg_db/data/examination/time/data.yaml +47 -47
  62. endoreg_db/data/examination/time-type/data.yaml +7 -7
  63. endoreg_db/data/examination/type/data.yaml +17 -5
  64. endoreg_db/data/examination_indication/endoscopy.yaml +424 -0
  65. endoreg_db/data/examination_indication_classification/endoscopy.yaml +160 -0
  66. endoreg_db/data/examination_indication_classification_choice/endoscopy.yaml +101 -0
  67. endoreg_db/data/examination_requirement_set/colonoscopy.yaml +15 -0
  68. endoreg_db/data/finding/anatomy_colon.yaml +128 -0
  69. endoreg_db/data/finding/colonoscopy.yaml +40 -0
  70. endoreg_db/data/finding/colonoscopy_bowel_prep.yaml +56 -0
  71. endoreg_db/data/finding/complication.yaml +16 -0
  72. endoreg_db/data/finding/data.yaml +105 -0
  73. endoreg_db/data/finding/examination_setting.yaml +16 -0
  74. endoreg_db/data/finding/medication_related.yaml +18 -0
  75. endoreg_db/data/finding/outcome.yaml +12 -0
  76. endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +95 -0
  77. endoreg_db/data/finding_classification/colonoscopy_jnet.yaml +22 -0
  78. endoreg_db/data/finding_classification/colonoscopy_kudo.yaml +25 -0
  79. endoreg_db/data/finding_classification/colonoscopy_lesion_circularity.yaml +20 -0
  80. endoreg_db/data/finding_classification/colonoscopy_lesion_planarity.yaml +24 -0
  81. endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +68 -0
  82. endoreg_db/data/finding_classification/colonoscopy_lesion_surface.yaml +20 -0
  83. endoreg_db/data/finding_classification/colonoscopy_location.yaml +80 -0
  84. endoreg_db/data/finding_classification/colonoscopy_lst.yaml +21 -0
  85. endoreg_db/data/finding_classification/colonoscopy_nice.yaml +20 -0
  86. endoreg_db/data/finding_classification/colonoscopy_paris.yaml +26 -0
  87. endoreg_db/data/finding_classification/colonoscopy_sano.yaml +22 -0
  88. endoreg_db/data/finding_classification/colonoscopy_summary.yaml +53 -0
  89. endoreg_db/data/finding_classification/complication_generic.yaml +25 -0
  90. endoreg_db/data/finding_classification/examination_setting_generic.yaml +40 -0
  91. endoreg_db/data/finding_classification/histology_colo.yaml +51 -0
  92. endoreg_db/data/finding_classification/intervention_required.yaml +26 -0
  93. endoreg_db/data/finding_classification/medication_related.yaml +23 -0
  94. endoreg_db/data/finding_classification/visualized.yaml +33 -0
  95. endoreg_db/data/finding_classification_choice/bowel_preparation.yaml +78 -0
  96. endoreg_db/data/finding_classification_choice/colon_lesion_circularity_default.yaml +32 -0
  97. endoreg_db/data/finding_classification_choice/colon_lesion_jnet.yaml +15 -0
  98. endoreg_db/data/finding_classification_choice/colon_lesion_kudo.yaml +23 -0
  99. endoreg_db/data/finding_classification_choice/colon_lesion_lst.yaml +15 -0
  100. endoreg_db/data/finding_classification_choice/colon_lesion_nice.yaml +17 -0
  101. endoreg_db/data/finding_classification_choice/colon_lesion_paris.yaml +57 -0
  102. endoreg_db/data/finding_classification_choice/colon_lesion_planarity_default.yaml +49 -0
  103. endoreg_db/data/finding_classification_choice/colon_lesion_sano.yaml +14 -0
  104. endoreg_db/data/finding_classification_choice/colon_lesion_surface_intact_default.yaml +36 -0
  105. endoreg_db/data/finding_classification_choice/colonoscopy_location.yaml +229 -0
  106. endoreg_db/data/finding_classification_choice/colonoscopy_not_complete_reason.yaml +19 -0
  107. endoreg_db/data/finding_classification_choice/colonoscopy_size.yaml +82 -0
  108. endoreg_db/data/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +15 -0
  109. endoreg_db/data/finding_classification_choice/complication_generic_types.yaml +15 -0
  110. endoreg_db/data/finding_classification_choice/examination_setting_generic_types.yaml +15 -0
  111. endoreg_db/data/finding_classification_choice/histology.yaml +24 -0
  112. endoreg_db/data/finding_classification_choice/histology_polyp.yaml +20 -0
  113. endoreg_db/data/finding_classification_choice/outcome.yaml +19 -0
  114. endoreg_db/data/finding_classification_choice/yes_no_na.yaml +11 -0
  115. endoreg_db/data/finding_classification_type/colonoscopy_basic.yaml +48 -0
  116. endoreg_db/data/finding_intervention/endoscopy.yaml +43 -0
  117. endoreg_db/data/finding_intervention/endoscopy_colonoscopy.yaml +168 -0
  118. endoreg_db/data/finding_intervention/endoscopy_egd.yaml +128 -0
  119. endoreg_db/data/finding_intervention/endoscopy_ercp.yaml +32 -0
  120. endoreg_db/data/finding_intervention/endoscopy_eus_lower.yaml +9 -0
  121. endoreg_db/data/finding_intervention/endoscopy_eus_upper.yaml +36 -0
  122. endoreg_db/data/finding_intervention_type/endoscopy.yaml +15 -0
  123. endoreg_db/data/finding_morphology_classification_type/colonoscopy.yaml +79 -0
  124. endoreg_db/data/finding_type/data.yaml +43 -0
  125. endoreg_db/data/gender/data.yaml +42 -18
  126. endoreg_db/data/information_source/annotation.yaml +6 -0
  127. endoreg_db/data/information_source/data.yaml +30 -30
  128. endoreg_db/data/information_source/endoscopy_guidelines.yaml +7 -0
  129. endoreg_db/data/information_source/medication.yaml +5 -5
  130. endoreg_db/data/information_source/prediction.yaml +7 -0
  131. endoreg_db/data/information_source_type/data.yaml +8 -0
  132. endoreg_db/data/lab_value/cardiac_enzymes.yaml +37 -31
  133. endoreg_db/data/lab_value/coagulation.yaml +54 -49
  134. endoreg_db/data/lab_value/electrolytes.yaml +228 -190
  135. endoreg_db/data/lab_value/gastrointestinal_function.yaml +133 -121
  136. endoreg_db/data/lab_value/hematology.yaml +184 -169
  137. endoreg_db/data/lab_value/hormones.yaml +59 -53
  138. endoreg_db/data/lab_value/lipids.yaml +53 -44
  139. endoreg_db/data/lab_value/misc.yaml +76 -30
  140. endoreg_db/data/lab_value/renal_function.yaml +12 -11
  141. endoreg_db/data/log_type/data.yaml +57 -0
  142. endoreg_db/data/lx_client_tag/base.yaml +54 -0
  143. endoreg_db/data/lx_client_type/base.yaml +30 -0
  144. endoreg_db/data/lx_permission/base.yaml +24 -0
  145. endoreg_db/data/lx_permission/endoreg.yaml +52 -0
  146. endoreg_db/data/medication/anticoagulation.yaml +64 -64
  147. endoreg_db/data/medication/tah.yaml +69 -69
  148. endoreg_db/data/medication_indication/anticoagulation.yaml +115 -120
  149. endoreg_db/data/medication_indication_type/data.yaml +10 -10
  150. endoreg_db/data/medication_indication_type/thrombembolism.yaml +40 -40
  151. endoreg_db/data/medication_intake_time/base.yaml +30 -30
  152. endoreg_db/data/medication_schedule/apixaban.yaml +94 -94
  153. endoreg_db/data/medication_schedule/ass.yaml +12 -12
  154. endoreg_db/data/medication_schedule/enoxaparin.yaml +26 -26
  155. endoreg_db/data/names_first/first_names.yaml +54 -0
  156. endoreg_db/data/names_last/last_names.yaml +51 -0
  157. endoreg_db/data/network_device/data.yaml +59 -0
  158. endoreg_db/data/network_device_type/data.yaml +12 -0
  159. endoreg_db/data/organ/data.yaml +29 -0
  160. endoreg_db/data/patient_lab_sample_type/generic.yaml +5 -5
  161. endoreg_db/data/pdf_type/data.yaml +46 -28
  162. endoreg_db/data/product/green_endoscopy_dashboard_Product.yaml +66 -66
  163. endoreg_db/data/product_group/green_endoscopy_dashboard_ProductGroup.yaml +33 -33
  164. endoreg_db/data/product_material/green_endoscopy_dashboard_ProductMaterial.yaml +308 -308
  165. endoreg_db/data/product_weight/green_endoscopy_dashboard_ProductWeight.yaml +88 -88
  166. endoreg_db/data/profession/data.yaml +70 -70
  167. endoreg_db/data/qualification/endoscopy.yaml +36 -0
  168. endoreg_db/data/qualification/m2.yaml +39 -0
  169. endoreg_db/data/qualification/outpatient_clinic.yaml +35 -0
  170. endoreg_db/data/qualification/sonography.yaml +36 -0
  171. endoreg_db/data/qualification_type/base.yaml +29 -0
  172. endoreg_db/data/reference_product/green_endoscopy_dashboard_ReferenceProduct.yaml +55 -55
  173. endoreg_db/data/report_reader_flag/rkh-histology-generic.yaml +10 -0
  174. endoreg_db/data/report_reader_flag/ukw-examination-generic.yaml +30 -26
  175. endoreg_db/data/report_reader_flag/ukw-histology-generic.yaml +24 -19
  176. endoreg_db/data/requirement/age.yaml +26 -0
  177. endoreg_db/data/requirement/colonoscopy_baseline_austria.yaml +45 -0
  178. endoreg_db/data/requirement/disease_cardiovascular.yaml +79 -0
  179. endoreg_db/data/requirement/disease_classification_choice_cardiovascular.yaml +41 -0
  180. endoreg_db/data/requirement/disease_hepatology.yaml +12 -0
  181. endoreg_db/data/requirement/disease_misc.yaml +12 -0
  182. endoreg_db/data/requirement/disease_renal.yaml +96 -0
  183. endoreg_db/data/requirement/endoscopy_bleeding_risk.yaml +59 -0
  184. endoreg_db/data/requirement/event_cardiology.yaml +251 -0
  185. endoreg_db/data/requirement/event_requirements.yaml +145 -0
  186. endoreg_db/data/requirement/finding_colon_polyp.yaml +50 -0
  187. endoreg_db/data/requirement/gender.yaml +25 -0
  188. endoreg_db/data/requirement/lab_value.yaml +441 -0
  189. endoreg_db/data/requirement/medication.yaml +93 -0
  190. endoreg_db/data/requirement_operator/age.yaml +13 -0
  191. endoreg_db/data/requirement_operator/lab_operators.yaml +129 -0
  192. endoreg_db/data/requirement_operator/model_operators.yaml +96 -0
  193. endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +48 -0
  194. endoreg_db/data/requirement_set/colonoscopy_austria_screening.yaml +57 -0
  195. endoreg_db/data/requirement_set/endoscopy_bleeding_risk.yaml +52 -0
  196. endoreg_db/data/requirement_set_type/data.yaml +20 -0
  197. endoreg_db/data/requirement_type/requirement_types.yaml +165 -0
  198. endoreg_db/data/resource/green_endoscopy_dashboard_Resource.yaml +15 -15
  199. endoreg_db/data/risk/bleeding.yaml +26 -0
  200. endoreg_db/data/risk/thrombosis.yaml +37 -0
  201. endoreg_db/data/risk_type/data.yaml +27 -0
  202. endoreg_db/data/setup_config.yaml +38 -0
  203. endoreg_db/data/shift/endoscopy.yaml +21 -0
  204. endoreg_db/data/shift/m2.yaml +0 -0
  205. endoreg_db/data/shift_type/base.yaml +35 -0
  206. endoreg_db/data/tag/requirement_set_tags.yaml +11 -0
  207. endoreg_db/data/transport_route/green_endoscopy_dashboard_TransportRoute.yaml +12 -12
  208. endoreg_db/data/unit/concentration.yaml +115 -92
  209. endoreg_db/data/unit/data.yaml +17 -17
  210. endoreg_db/data/unit/length.yaml +30 -30
  211. endoreg_db/data/unit/misc.yaml +19 -19
  212. endoreg_db/data/unit/rate.yaml +5 -5
  213. endoreg_db/data/unit/time.yaml +48 -13
  214. endoreg_db/data/unit/volume.yaml +35 -35
  215. endoreg_db/data/unit/weight.yaml +37 -37
  216. endoreg_db/data/waste/data.yaml +11 -11
  217. endoreg_db/exceptions.py +19 -0
  218. endoreg_db/factories/__init__.py +0 -0
  219. endoreg_db/forms/__init__.py +5 -3
  220. endoreg_db/forms/examination_form.py +11 -0
  221. endoreg_db/forms/patient_finding_intervention_form.py +18 -0
  222. endoreg_db/forms/patient_form.py +27 -0
  223. endoreg_db/forms/questionnaires/__init__.py +1 -1
  224. endoreg_db/forms/questionnaires/tto_questionnaire.py +23 -23
  225. endoreg_db/forms/settings/__init__.py +8 -8
  226. endoreg_db/forms/unit.py +5 -5
  227. endoreg_db/helpers/__init__.py +0 -0
  228. endoreg_db/helpers/count_db.py +45 -0
  229. endoreg_db/helpers/data_loader.py +208 -0
  230. endoreg_db/helpers/default_objects.py +378 -0
  231. endoreg_db/helpers/download_segmentation_model.py +31 -0
  232. endoreg_db/helpers/interact.py +6 -0
  233. endoreg_db/helpers/test_video_helper.py +119 -0
  234. endoreg_db/logger_conf.py +140 -0
  235. endoreg_db/management/__init__.py +1 -0
  236. endoreg_db/management/commands/__init__.py +1 -0
  237. endoreg_db/management/commands/anonymize_video.py +0 -0
  238. endoreg_db/management/commands/check_auth.py +125 -0
  239. endoreg_db/management/commands/create_model_meta_from_huggingface.py +115 -0
  240. endoreg_db/management/commands/create_multilabel_model_meta.py +214 -0
  241. endoreg_db/management/commands/fix_missing_patient_data.py +172 -0
  242. endoreg_db/management/commands/fix_video_paths.py +165 -0
  243. endoreg_db/management/commands/import_fallback_video.py +203 -0
  244. endoreg_db/management/commands/import_report.py +298 -0
  245. endoreg_db/management/commands/import_video.py +423 -0
  246. endoreg_db/management/commands/import_video_with_classification.py +367 -0
  247. endoreg_db/management/commands/init_default_ai_model.py +112 -0
  248. endoreg_db/management/commands/load_ai_model_data.py +77 -45
  249. endoreg_db/management/commands/load_ai_model_label_data.py +59 -0
  250. endoreg_db/management/commands/load_base_db_data.py +192 -128
  251. endoreg_db/management/commands/load_center_data.py +68 -43
  252. endoreg_db/management/commands/{load_medication_intake_time_data.py → load_contraindication_data.py} +40 -40
  253. endoreg_db/management/commands/load_disease_classification_choices_data.py +40 -40
  254. endoreg_db/management/commands/load_disease_classification_data.py +40 -40
  255. endoreg_db/management/commands/load_disease_data.py +61 -39
  256. endoreg_db/management/commands/load_distribution_data.py +65 -65
  257. endoreg_db/management/commands/{load_endoscope_type_data.py → load_endoscope_data.py} +67 -44
  258. endoreg_db/management/commands/load_event_data.py +40 -40
  259. endoreg_db/management/commands/load_examination_data.py +74 -74
  260. endoreg_db/management/commands/load_examination_indication_data.py +86 -0
  261. endoreg_db/management/commands/load_finding_data.py +128 -0
  262. endoreg_db/management/commands/load_gender_data.py +43 -43
  263. endoreg_db/management/commands/load_green_endoscopy_wuerzburg_data.py +131 -132
  264. endoreg_db/management/commands/load_information_source.py +50 -44
  265. endoreg_db/management/commands/load_lab_value_data.py +49 -49
  266. endoreg_db/management/commands/load_medication_data.py +103 -41
  267. endoreg_db/management/commands/load_name_data.py +37 -0
  268. endoreg_db/management/commands/{load_medication_indication_type_data.py → load_organ_data.py} +42 -40
  269. endoreg_db/management/commands/load_pdf_type_data.py +60 -60
  270. endoreg_db/management/commands/load_profession_data.py +43 -43
  271. endoreg_db/management/commands/load_qualification_data.py +59 -0
  272. endoreg_db/management/commands/{load_report_reader_flag.py → load_report_reader_flag_data.py} +45 -45
  273. endoreg_db/management/commands/load_requirement_data.py +180 -0
  274. endoreg_db/management/commands/load_risk_data.py +56 -0
  275. endoreg_db/management/commands/load_shift_data.py +60 -0
  276. endoreg_db/management/commands/load_tag_data.py +57 -0
  277. endoreg_db/management/commands/load_unit_data.py +45 -45
  278. endoreg_db/management/commands/load_user_groups.py +28 -28
  279. endoreg_db/management/commands/register_ai_model.py +64 -65
  280. endoreg_db/management/commands/reset_celery_schedule.py +9 -9
  281. endoreg_db/management/commands/setup_endoreg_db.py +381 -0
  282. endoreg_db/management/commands/start_filewatcher.py +106 -0
  283. endoreg_db/management/commands/storage_management.py +548 -0
  284. endoreg_db/management/commands/summarize_db_content.py +189 -0
  285. endoreg_db/management/commands/validate_video.py +204 -0
  286. endoreg_db/management/commands/validate_video_files.py +161 -0
  287. endoreg_db/management/commands/video_validation.py +22 -0
  288. endoreg_db/mermaid/Overall_flow_patient_finding_intervention.md +10 -0
  289. endoreg_db/mermaid/anonymized_image_annotation.md +20 -0
  290. endoreg_db/mermaid/binary_classification_annotation.md +50 -0
  291. endoreg_db/mermaid/classification.md +8 -0
  292. endoreg_db/mermaid/examination.md +8 -0
  293. endoreg_db/mermaid/findings.md +7 -0
  294. endoreg_db/mermaid/image_classification.md +28 -0
  295. endoreg_db/mermaid/interventions.md +8 -0
  296. endoreg_db/mermaid/morphology.md +8 -0
  297. endoreg_db/mermaid/patient_creation.md +14 -0
  298. endoreg_db/mermaid/video_segmentation_annotation.md +17 -0
  299. endoreg_db/migrations/0001_initial.py +1857 -582
  300. endoreg_db/migrations/0002_add_video_correction_models.py +52 -0
  301. endoreg_db/migrations/0003_add_center_display_name.py +30 -0
  302. endoreg_db/models/__init__.py +359 -74
  303. endoreg_db/models/administration/__init__.py +116 -0
  304. endoreg_db/models/administration/ai/__init__.py +9 -0
  305. endoreg_db/models/administration/ai/active_model.py +35 -0
  306. endoreg_db/models/administration/ai/ai_model.py +156 -0
  307. endoreg_db/models/{ai_model → administration/ai}/model_type.py +41 -26
  308. endoreg_db/models/administration/case/__init__.py +19 -0
  309. endoreg_db/models/administration/case/case.py +114 -0
  310. endoreg_db/models/{case_template → administration/case/case_template}/__init__.py +15 -6
  311. endoreg_db/models/{case_template → administration/case/case_template}/case_template.py +125 -81
  312. endoreg_db/models/{case_template → administration/case/case_template}/case_template_rule.py +269 -276
  313. endoreg_db/models/{case_template → administration/case/case_template}/case_template_rule_value.py +86 -73
  314. endoreg_db/models/{case_template → administration/case/case_template}/case_template_type.py +26 -28
  315. endoreg_db/models/{center → administration/center}/__init__.py +13 -4
  316. endoreg_db/models/administration/center/center.py +67 -0
  317. endoreg_db/models/administration/center/center_product.py +64 -0
  318. endoreg_db/models/administration/center/center_resource.py +49 -0
  319. endoreg_db/models/administration/center/center_shift.py +88 -0
  320. endoreg_db/models/administration/center/center_waste.py +30 -0
  321. endoreg_db/models/administration/permissions/__init__.py +44 -0
  322. endoreg_db/models/administration/person/__init__.py +24 -0
  323. endoreg_db/models/administration/person/employee/__init__.py +3 -0
  324. endoreg_db/models/administration/person/employee/employee.py +35 -0
  325. endoreg_db/models/administration/person/employee/employee_qualification.py +39 -0
  326. endoreg_db/models/administration/person/employee/employee_type.py +42 -0
  327. endoreg_db/models/administration/person/examiner/__init__.py +4 -0
  328. endoreg_db/models/administration/person/examiner/examiner.py +54 -0
  329. endoreg_db/models/administration/person/names/__init__.py +0 -0
  330. endoreg_db/models/{persons → administration/person/names}/first_name.py +18 -18
  331. endoreg_db/models/{persons → administration/person/names}/last_name.py +18 -19
  332. endoreg_db/models/administration/person/patient/__init__.py +5 -0
  333. endoreg_db/models/administration/person/patient/patient.py +460 -0
  334. endoreg_db/models/{persons → administration/person}/person.py +31 -31
  335. endoreg_db/models/administration/person/profession/__init__.py +24 -0
  336. endoreg_db/models/administration/person/user/__init__.py +5 -0
  337. endoreg_db/models/administration/person/user/portal_user_information.py +37 -0
  338. endoreg_db/models/administration/product/__init__.py +14 -0
  339. endoreg_db/models/administration/product/product.py +97 -0
  340. endoreg_db/models/administration/product/product_group.py +39 -0
  341. endoreg_db/models/administration/product/product_material.py +54 -0
  342. endoreg_db/models/{product → administration/product}/product_weight.py +47 -26
  343. endoreg_db/models/{product → administration/product}/reference_product.py +130 -99
  344. endoreg_db/models/administration/qualification/__init__.py +7 -0
  345. endoreg_db/models/administration/qualification/qualification.py +37 -0
  346. endoreg_db/models/administration/qualification/qualification_type.py +35 -0
  347. endoreg_db/models/administration/shift/__init__.py +9 -0
  348. endoreg_db/models/administration/shift/scheduled_days.py +69 -0
  349. endoreg_db/models/administration/shift/shift.py +51 -0
  350. endoreg_db/models/administration/shift/shift_type.py +108 -0
  351. endoreg_db/models/label/__init__.py +24 -1
  352. endoreg_db/models/label/annotation/__init__.py +12 -0
  353. endoreg_db/models/label/annotation/image_classification.py +84 -0
  354. endoreg_db/models/label/annotation/video_segmentation_annotation.py +66 -0
  355. endoreg_db/models/label/label.py +83 -84
  356. endoreg_db/models/label/label_set.py +53 -0
  357. endoreg_db/models/label/label_type.py +29 -0
  358. endoreg_db/models/label/label_video_segment/__init__.py +3 -0
  359. endoreg_db/models/label/label_video_segment/_create_from_video.py +41 -0
  360. endoreg_db/models/label/label_video_segment/label_video_segment.py +511 -0
  361. endoreg_db/models/label/video_segmentation_label.py +31 -0
  362. endoreg_db/models/label/video_segmentation_labelset.py +27 -0
  363. endoreg_db/models/media/__init__.py +16 -0
  364. endoreg_db/models/media/frame/__init__.py +3 -0
  365. endoreg_db/models/media/frame/frame.py +111 -0
  366. endoreg_db/models/media/pdf/__init__.py +11 -0
  367. endoreg_db/models/media/pdf/raw_pdf.py +757 -0
  368. endoreg_db/models/media/pdf/report_file.py +162 -0
  369. endoreg_db/models/media/pdf/report_reader/__init__.py +7 -0
  370. endoreg_db/models/media/pdf/report_reader/report_reader_config.py +77 -0
  371. endoreg_db/models/{report_reader → media/pdf/report_reader}/report_reader_flag.py +19 -19
  372. endoreg_db/models/media/video/__init__.py +8 -0
  373. endoreg_db/models/media/video/create_from_file.py +358 -0
  374. endoreg_db/models/media/video/pipe_1.py +213 -0
  375. endoreg_db/models/media/video/pipe_2.py +105 -0
  376. endoreg_db/models/media/video/refactor_plan.md +0 -0
  377. endoreg_db/models/media/video/video_file.py +825 -0
  378. endoreg_db/models/media/video/video_file_ai.py +443 -0
  379. endoreg_db/models/media/video/video_file_anonymize.py +349 -0
  380. endoreg_db/models/media/video/video_file_frames/__init__.py +47 -0
  381. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +22 -0
  382. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +23 -0
  383. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +104 -0
  384. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +174 -0
  385. endoreg_db/models/media/video/video_file_frames/_get_frame.py +28 -0
  386. endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +27 -0
  387. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +20 -0
  388. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +27 -0
  389. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +34 -0
  390. endoreg_db/models/media/video/video_file_frames/_get_frames.py +27 -0
  391. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +129 -0
  392. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +141 -0
  393. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +65 -0
  394. endoreg_db/models/media/video/video_file_frames.py +0 -0
  395. endoreg_db/models/media/video/video_file_io.py +168 -0
  396. endoreg_db/models/media/video/video_file_meta/__init__.py +22 -0
  397. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +45 -0
  398. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +39 -0
  399. endoreg_db/models/media/video/video_file_meta/get_fps.py +147 -0
  400. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +143 -0
  401. endoreg_db/models/media/video/video_file_meta/text_meta.py +134 -0
  402. endoreg_db/models/media/video/video_file_meta/video_meta.py +70 -0
  403. endoreg_db/models/media/video/video_file_segments.py +209 -0
  404. endoreg_db/models/media/video/video_metadata.py +65 -0
  405. endoreg_db/models/media/video/video_processing.py +152 -0
  406. endoreg_db/models/medical/__init__.py +146 -0
  407. endoreg_db/models/medical/contraindication/__init__.py +17 -0
  408. endoreg_db/models/medical/disease.py +156 -0
  409. endoreg_db/models/medical/event.py +137 -0
  410. endoreg_db/models/medical/examination/__init__.py +9 -0
  411. endoreg_db/models/medical/examination/examination.py +148 -0
  412. endoreg_db/models/medical/examination/examination_indication.py +278 -0
  413. endoreg_db/models/medical/examination/examination_time.py +49 -0
  414. endoreg_db/models/medical/examination/examination_time_type.py +41 -0
  415. endoreg_db/models/medical/examination/examination_type.py +48 -0
  416. endoreg_db/models/medical/finding/__init__.py +18 -0
  417. endoreg_db/models/medical/finding/finding.py +96 -0
  418. endoreg_db/models/medical/finding/finding_classification.py +142 -0
  419. endoreg_db/models/medical/finding/finding_intervention.py +52 -0
  420. endoreg_db/models/medical/finding/finding_type.py +35 -0
  421. endoreg_db/models/medical/hardware/__init__.py +8 -0
  422. endoreg_db/models/medical/hardware/endoscope.py +65 -0
  423. endoreg_db/models/{hardware → medical/hardware}/endoscopy_processor.py +182 -143
  424. endoreg_db/models/medical/laboratory/__init__.py +5 -0
  425. endoreg_db/models/medical/laboratory/lab_value.py +419 -0
  426. endoreg_db/models/medical/medication/__init__.py +19 -0
  427. endoreg_db/models/medical/medication/medication.py +31 -0
  428. endoreg_db/models/medical/medication/medication_indication.py +50 -0
  429. endoreg_db/models/medical/medication/medication_indication_type.py +39 -0
  430. endoreg_db/models/medical/medication/medication_intake_time.py +44 -0
  431. endoreg_db/models/medical/medication/medication_schedule.py +45 -0
  432. endoreg_db/models/medical/organ/__init__.py +35 -0
  433. endoreg_db/models/medical/patient/__init__.py +56 -0
  434. endoreg_db/models/medical/patient/medication_examples.py +38 -0
  435. endoreg_db/models/medical/patient/patient_disease.py +63 -0
  436. endoreg_db/models/medical/patient/patient_event.py +75 -0
  437. endoreg_db/models/medical/patient/patient_examination.py +249 -0
  438. endoreg_db/models/medical/patient/patient_examination_indication.py +44 -0
  439. endoreg_db/models/medical/patient/patient_finding.py +357 -0
  440. endoreg_db/models/medical/patient/patient_finding_classification.py +207 -0
  441. endoreg_db/models/medical/patient/patient_finding_intervention.py +40 -0
  442. endoreg_db/models/medical/patient/patient_lab_sample.py +148 -0
  443. endoreg_db/models/{persons → medical}/patient/patient_lab_value.py +222 -176
  444. endoreg_db/models/medical/patient/patient_medication.py +104 -0
  445. endoreg_db/models/medical/patient/patient_medication_schedule.py +136 -0
  446. endoreg_db/models/medical/risk/__init__.py +7 -0
  447. endoreg_db/models/medical/risk/risk.py +72 -0
  448. endoreg_db/models/medical/risk/risk_type.py +51 -0
  449. endoreg_db/models/metadata/__init__.py +19 -0
  450. endoreg_db/models/metadata/frame_ocr_result.py +0 -0
  451. endoreg_db/models/metadata/model_meta.py +206 -0
  452. endoreg_db/models/metadata/model_meta_logic.py +343 -0
  453. endoreg_db/models/{data_file/metadata → metadata}/pdf_meta.py +89 -70
  454. endoreg_db/models/metadata/sensitive_meta.py +288 -0
  455. endoreg_db/models/metadata/sensitive_meta_logic.py +1048 -0
  456. endoreg_db/models/metadata/video_meta.py +332 -0
  457. endoreg_db/models/metadata/video_prediction_logic.py +190 -0
  458. endoreg_db/models/metadata/video_prediction_meta.py +270 -0
  459. endoreg_db/models/other/__init__.py +40 -5
  460. endoreg_db/models/other/distribution/__init__.py +44 -0
  461. endoreg_db/models/other/distribution/base_value_distribution.py +20 -0
  462. endoreg_db/models/other/distribution/date_value_distribution.py +89 -0
  463. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +32 -0
  464. endoreg_db/models/other/distribution/numeric_value_distribution.py +125 -0
  465. endoreg_db/models/other/distribution/single_categorical_value_distribution.py +22 -0
  466. endoreg_db/models/other/emission/__init__.py +5 -0
  467. endoreg_db/models/other/emission/emission_factor.py +94 -0
  468. endoreg_db/models/{persons → other}/gender.py +27 -22
  469. endoreg_db/models/other/information_source.py +159 -0
  470. endoreg_db/models/other/material.py +28 -16
  471. endoreg_db/models/other/resource.py +21 -17
  472. endoreg_db/models/other/tag.py +27 -0
  473. endoreg_db/models/other/transport_route.py +33 -21
  474. endoreg_db/models/{unit.py → other/unit.py} +32 -22
  475. endoreg_db/models/other/waste.py +27 -20
  476. endoreg_db/models/requirement/__init__.py +11 -0
  477. endoreg_db/models/requirement/requirement.py +767 -0
  478. endoreg_db/models/requirement/requirement_evaluation/__init__.py +6 -0
  479. endoreg_db/models/requirement/requirement_evaluation/get_values.py +40 -0
  480. endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +9 -0
  481. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +95 -0
  482. endoreg_db/models/requirement/requirement_operator.py +176 -0
  483. endoreg_db/models/requirement/requirement_set.py +287 -0
  484. endoreg_db/models/rule/__init__.py +13 -0
  485. endoreg_db/models/{rules → rule}/rule.py +27 -24
  486. endoreg_db/models/{rules → rule}/rule_applicator.py +224 -224
  487. endoreg_db/models/{rules → rule}/rule_attribute_dtype.py +16 -18
  488. endoreg_db/models/{rules → rule}/rule_type.py +19 -21
  489. endoreg_db/models/{rules → rule}/ruleset.py +17 -19
  490. endoreg_db/models/state/__init__.py +12 -0
  491. endoreg_db/models/state/abstract.py +11 -0
  492. endoreg_db/models/state/audit_ledger.py +150 -0
  493. endoreg_db/models/state/label_video_segment.py +22 -0
  494. endoreg_db/models/state/raw_pdf.py +187 -0
  495. endoreg_db/models/state/sensitive_meta.py +46 -0
  496. endoreg_db/models/state/video.py +232 -0
  497. endoreg_db/models/upload_job.py +99 -0
  498. endoreg_db/models/utils.py +135 -0
  499. endoreg_db/queries/__init__.py +4 -4
  500. endoreg_db/queries/annotations/__init__.py +2 -2
  501. endoreg_db/queries/annotations/legacy.py +158 -159
  502. endoreg_db/renames.yml +8 -0
  503. endoreg_db/root_urls.py +9 -0
  504. endoreg_db/schemas/__init__.py +0 -0
  505. endoreg_db/schemas/examination_evaluation.py +27 -0
  506. endoreg_db/serializers/Frames_NICE_and_PARIS_classifications.py +775 -0
  507. endoreg_db/serializers/__init__.py +118 -10
  508. endoreg_db/serializers/_old/raw_pdf_meta_validation.py +223 -0
  509. endoreg_db/serializers/_old/raw_video_meta_validation.py +179 -0
  510. endoreg_db/serializers/_old/video.py +71 -0
  511. endoreg_db/serializers/administration/__init__.py +14 -0
  512. endoreg_db/serializers/administration/ai/__init__.py +10 -0
  513. endoreg_db/serializers/administration/ai/active_model.py +10 -0
  514. endoreg_db/serializers/administration/ai/ai_model.py +18 -0
  515. endoreg_db/serializers/administration/ai/model_type.py +10 -0
  516. endoreg_db/serializers/administration/center.py +9 -0
  517. endoreg_db/serializers/administration/gender.py +9 -0
  518. endoreg_db/serializers/anonymization.py +69 -0
  519. endoreg_db/serializers/evaluation/examination_evaluation.py +1 -0
  520. endoreg_db/serializers/examination/__init__.py +10 -0
  521. endoreg_db/serializers/examination/base.py +46 -0
  522. endoreg_db/serializers/examination/dropdown.py +21 -0
  523. endoreg_db/serializers/examination_serializer.py +12 -0
  524. endoreg_db/serializers/finding/__init__.py +5 -0
  525. endoreg_db/serializers/finding/finding.py +54 -0
  526. endoreg_db/serializers/finding_classification/__init__.py +7 -0
  527. endoreg_db/serializers/finding_classification/choice.py +19 -0
  528. endoreg_db/serializers/finding_classification/classification.py +13 -0
  529. endoreg_db/serializers/label/__init__.py +7 -0
  530. endoreg_db/serializers/label/image_classification_annotation.py +62 -0
  531. endoreg_db/serializers/label/label.py +15 -0
  532. endoreg_db/serializers/label_video_segment/__init__.py +7 -0
  533. endoreg_db/serializers/label_video_segment/_lvs_create.py +149 -0
  534. endoreg_db/serializers/label_video_segment/_lvs_update.py +138 -0
  535. endoreg_db/serializers/label_video_segment/_lvs_validate.py +149 -0
  536. endoreg_db/serializers/label_video_segment/label_video_segment.py +344 -0
  537. endoreg_db/serializers/label_video_segment/label_video_segment_annotation.py +99 -0
  538. endoreg_db/serializers/label_video_segment/label_video_segment_update.py +163 -0
  539. endoreg_db/serializers/meta/__init__.py +19 -0
  540. endoreg_db/serializers/meta/pdf_file_meta_extraction.py +115 -0
  541. endoreg_db/serializers/meta/report_meta.py +53 -0
  542. endoreg_db/serializers/meta/sensitive_meta_detail.py +162 -0
  543. endoreg_db/serializers/meta/sensitive_meta_update.py +148 -0
  544. endoreg_db/serializers/meta/sensitive_meta_verification.py +59 -0
  545. endoreg_db/serializers/meta/video_meta.py +39 -0
  546. endoreg_db/serializers/misc/__init__.py +14 -0
  547. endoreg_db/serializers/misc/file_overview.py +182 -0
  548. endoreg_db/serializers/misc/sensitive_patient_data.py +120 -0
  549. endoreg_db/serializers/misc/stats.py +33 -0
  550. endoreg_db/serializers/misc/translatable_field_mix_in.py +44 -0
  551. endoreg_db/serializers/misc/upload_job.py +71 -0
  552. endoreg_db/serializers/patient/__init__.py +11 -0
  553. endoreg_db/serializers/patient/patient.py +86 -0
  554. endoreg_db/serializers/patient/patient_dropdown.py +27 -0
  555. endoreg_db/serializers/patient_examination/__init__.py +7 -0
  556. endoreg_db/serializers/patient_examination/patient_examination.py +141 -0
  557. endoreg_db/serializers/patient_finding/__init__.py +15 -0
  558. endoreg_db/serializers/patient_finding/patient_finding.py +31 -0
  559. endoreg_db/serializers/patient_finding/patient_finding_classification.py +39 -0
  560. endoreg_db/serializers/patient_finding/patient_finding_detail.py +53 -0
  561. endoreg_db/serializers/patient_finding/patient_finding_intervention.py +26 -0
  562. endoreg_db/serializers/patient_finding/patient_finding_list.py +41 -0
  563. endoreg_db/serializers/patient_finding/patient_finding_write.py +126 -0
  564. endoreg_db/serializers/pdf/__init__.py +5 -0
  565. endoreg_db/serializers/pdf/anony_text_validation.py +85 -0
  566. endoreg_db/serializers/report/__init__.py +9 -0
  567. endoreg_db/serializers/report/mixins.py +45 -0
  568. endoreg_db/serializers/report/report.py +105 -0
  569. endoreg_db/serializers/report/report_list.py +22 -0
  570. endoreg_db/serializers/report/secure_file_url.py +26 -0
  571. endoreg_db/serializers/requirements/requirement_schema.py +25 -0
  572. endoreg_db/serializers/requirements/requirement_sets.py +29 -0
  573. endoreg_db/serializers/sensitive_meta_serializer.py +282 -0
  574. endoreg_db/serializers/video/__init__.py +7 -0
  575. endoreg_db/serializers/video/segmentation.py +263 -0
  576. endoreg_db/serializers/video/video_file_brief.py +10 -0
  577. endoreg_db/serializers/video/video_file_detail.py +83 -0
  578. endoreg_db/serializers/video/video_file_list.py +67 -0
  579. endoreg_db/serializers/video/video_metadata.py +105 -0
  580. endoreg_db/serializers/video/video_processing_history.py +153 -0
  581. endoreg_db/serializers/video_examination.py +198 -0
  582. endoreg_db/services/__init__.py +5 -0
  583. endoreg_db/services/anonymization.py +223 -0
  584. endoreg_db/services/examination_evaluation.py +149 -0
  585. endoreg_db/services/finding_description_service.py +0 -0
  586. endoreg_db/services/lookup_service.py +411 -0
  587. endoreg_db/services/lookup_store.py +266 -0
  588. endoreg_db/services/pdf_import.py +1382 -0
  589. endoreg_db/services/polling_coordinator.py +288 -0
  590. endoreg_db/services/pseudonym_service.py +89 -0
  591. endoreg_db/services/requirements_object.py +147 -0
  592. endoreg_db/services/segment_sync.py +155 -0
  593. endoreg_db/services/storage_aware_video_processor.py +344 -0
  594. endoreg_db/services/video_import.py +1259 -0
  595. endoreg_db/tasks/upload_tasks.py +207 -0
  596. endoreg_db/tasks/video_ingest.py +157 -0
  597. endoreg_db/tasks/video_processing_tasks.py +327 -0
  598. endoreg_db/templates/admin/patient_finding_intervention.html +253 -0
  599. endoreg_db/templates/admin/start_examination.html +12 -0
  600. endoreg_db/templates/timeline.html +176 -0
  601. endoreg_db/urls/__init__.py +83 -0
  602. endoreg_db/urls/anonymization.py +32 -0
  603. endoreg_db/urls/auth.py +16 -0
  604. endoreg_db/urls/classification.py +39 -0
  605. endoreg_db/urls/examination.py +54 -0
  606. endoreg_db/urls/files.py +6 -0
  607. endoreg_db/urls/label_video_segment_validate.py +33 -0
  608. endoreg_db/urls/label_video_segments.py +46 -0
  609. endoreg_db/urls/media.py +227 -0
  610. endoreg_db/urls/patient.py +19 -0
  611. endoreg_db/urls/report.py +48 -0
  612. endoreg_db/urls/requirements.py +13 -0
  613. endoreg_db/urls/sensitive_meta.py +0 -0
  614. endoreg_db/urls/stats.py +46 -0
  615. endoreg_db/urls/upload.py +20 -0
  616. endoreg_db/urls/video.py +61 -0
  617. endoreg_db/urls.py +9 -0
  618. endoreg_db/utils/__init__.py +88 -1
  619. endoreg_db/utils/ai/__init__.py +9 -0
  620. endoreg_db/{models/ai_model/utils.py → utils/ai/get.py} +5 -8
  621. endoreg_db/utils/ai/inference_dataset.py +52 -0
  622. endoreg_db/utils/ai/multilabel_classification_net.py +159 -0
  623. endoreg_db/utils/ai/postprocess.py +63 -0
  624. endoreg_db/utils/ai/predict.py +291 -0
  625. endoreg_db/utils/ai/preprocess.py +68 -0
  626. endoreg_db/utils/calc_duration_seconds.py +24 -0
  627. endoreg_db/utils/case_generator/__init__.py +0 -0
  628. endoreg_db/utils/case_generator/case_generator.py +159 -0
  629. endoreg_db/utils/case_generator/lab_sample_factory.py +33 -0
  630. endoreg_db/utils/case_generator/utils.py +30 -0
  631. endoreg_db/utils/check_video_files.py +148 -0
  632. endoreg_db/utils/cropping.py +28 -28
  633. endoreg_db/utils/dataloader.py +175 -92
  634. endoreg_db/utils/dates.py +60 -0
  635. endoreg_db/utils/env.py +33 -0
  636. endoreg_db/utils/extract_specific_frames.py +72 -0
  637. endoreg_db/utils/file_operations.py +58 -30
  638. endoreg_db/utils/fix_video_path_direct.py +141 -0
  639. endoreg_db/utils/frame_anonymization_utils.py +463 -0
  640. endoreg_db/utils/hashs.py +153 -34
  641. endoreg_db/utils/links/__init__.py +0 -0
  642. endoreg_db/utils/links/requirement_link.py +193 -0
  643. endoreg_db/utils/mime_types.py +0 -0
  644. endoreg_db/utils/names.py +76 -0
  645. endoreg_db/utils/ocr.py +190 -197
  646. endoreg_db/utils/parse_and_generate_yaml.py +46 -0
  647. endoreg_db/utils/paths.py +95 -0
  648. endoreg_db/utils/permissions.py +143 -0
  649. endoreg_db/utils/pipelines/Readme.md +235 -0
  650. endoreg_db/utils/pipelines/__init__.py +0 -0
  651. endoreg_db/utils/pipelines/process_video_dir.py +120 -0
  652. endoreg_db/utils/product/__init__.py +0 -0
  653. endoreg_db/utils/product/sum_emissions.py +20 -0
  654. endoreg_db/utils/product/sum_weights.py +18 -0
  655. endoreg_db/utils/pydantic_models/__init__.py +6 -0
  656. endoreg_db/utils/pydantic_models/db_config.py +57 -0
  657. endoreg_db/utils/requirement_helpers.py +0 -0
  658. endoreg_db/utils/requirement_operator_logic/__init__.py +0 -0
  659. endoreg_db/utils/requirement_operator_logic/lab_value_operators.py +578 -0
  660. endoreg_db/utils/requirement_operator_logic/model_evaluators.py +368 -0
  661. endoreg_db/utils/setup_config.py +177 -0
  662. endoreg_db/utils/translation.py +27 -0
  663. endoreg_db/utils/uuid.py +4 -4
  664. endoreg_db/utils/validate_endo_roi.py +19 -0
  665. endoreg_db/utils/validate_subcategory_dict.py +91 -0
  666. endoreg_db/utils/validate_video_detailed.py +357 -0
  667. endoreg_db/utils/video/__init__.py +26 -0
  668. endoreg_db/utils/video/extract_frames.py +88 -0
  669. endoreg_db/utils/video/ffmpeg_wrapper.py +835 -0
  670. endoreg_db/utils/video/names.py +42 -0
  671. endoreg_db/utils/video/streaming_processor.py +312 -0
  672. endoreg_db/utils/video/video_splitter.py +94 -0
  673. endoreg_db/views/Frames_NICE_and_PARIS_classifications_views.py +238 -0
  674. endoreg_db/views/__init__.py +275 -0
  675. endoreg_db/views/anonymization/__init__.py +27 -0
  676. endoreg_db/views/anonymization/media_management.py +454 -0
  677. endoreg_db/views/anonymization/overview.py +216 -0
  678. endoreg_db/views/anonymization/validate.py +107 -0
  679. endoreg_db/views/auth/__init__.py +13 -0
  680. endoreg_db/views/auth/keycloak.py +113 -0
  681. endoreg_db/views/examination/__init__.py +33 -0
  682. endoreg_db/views/examination/examination.py +37 -0
  683. endoreg_db/views/examination/examination_manifest_cache.py +26 -0
  684. endoreg_db/views/examination/get_finding_classification_choices.py +59 -0
  685. endoreg_db/views/examination/get_finding_classifications.py +36 -0
  686. endoreg_db/views/examination/get_findings.py +41 -0
  687. endoreg_db/views/examination/get_instruments.py +18 -0
  688. endoreg_db/views/examination/get_interventions.py +14 -0
  689. endoreg_db/views/finding/__init__.py +9 -0
  690. endoreg_db/views/finding/finding.py +112 -0
  691. endoreg_db/views/finding/get_classifications.py +14 -0
  692. endoreg_db/views/finding/get_interventions.py +17 -0
  693. endoreg_db/views/finding_classification/__init__.py +13 -0
  694. endoreg_db/views/finding_classification/base.py +0 -0
  695. endoreg_db/views/finding_classification/finding_classification.py +42 -0
  696. endoreg_db/views/finding_classification/get_classification_choices.py +55 -0
  697. endoreg_db/views/label/__init__.py +5 -0
  698. endoreg_db/views/label/label.py +15 -0
  699. endoreg_db/views/label_video_segment/__init__.py +16 -0
  700. endoreg_db/views/label_video_segment/create_lvs_from_annotation.py +44 -0
  701. endoreg_db/views/label_video_segment/get_lvs_by_name_and_video.py +50 -0
  702. endoreg_db/views/label_video_segment/label_video_segment.py +77 -0
  703. endoreg_db/views/label_video_segment/label_video_segment_by_label.py +174 -0
  704. endoreg_db/views/label_video_segment/label_video_segment_detail.py +73 -0
  705. endoreg_db/views/label_video_segment/update_lvs_from_annotation.py +46 -0
  706. endoreg_db/views/label_video_segment/validate.py +226 -0
  707. endoreg_db/views/media/__init__.py +45 -0
  708. endoreg_db/views/media/pdf_media.py +388 -0
  709. endoreg_db/views/media/segments.py +71 -0
  710. endoreg_db/views/media/sensitive_metadata.py +314 -0
  711. endoreg_db/views/media/video_media.py +272 -0
  712. endoreg_db/views/media/video_segments.py +524 -0
  713. endoreg_db/views/meta/__init__.py +15 -0
  714. endoreg_db/views/meta/available_files_list.py +146 -0
  715. endoreg_db/views/meta/report_meta.py +53 -0
  716. endoreg_db/views/meta/sensitive_meta_detail.py +148 -0
  717. endoreg_db/views/meta/sensitive_meta_list.py +104 -0
  718. endoreg_db/views/meta/sensitive_meta_verification.py +71 -0
  719. endoreg_db/views/misc/__init__.py +63 -0
  720. endoreg_db/views/misc/center.py +13 -0
  721. endoreg_db/views/misc/csrf.py +7 -0
  722. endoreg_db/views/misc/gender.py +14 -0
  723. endoreg_db/views/misc/secure_file_serving_view.py +80 -0
  724. endoreg_db/views/misc/secure_file_url_view.py +84 -0
  725. endoreg_db/views/misc/secure_url_validate.py +79 -0
  726. endoreg_db/views/misc/stats.py +220 -0
  727. endoreg_db/views/misc/translation.py +182 -0
  728. endoreg_db/views/misc/upload_views.py +240 -0
  729. endoreg_db/views/patient/__init__.py +5 -0
  730. endoreg_db/views/patient/patient.py +210 -0
  731. endoreg_db/views/patient_examination/DEPRECATED_video_backup.py +164 -0
  732. endoreg_db/views/patient_examination/__init__.py +11 -0
  733. endoreg_db/views/patient_examination/patient_examination.py +140 -0
  734. endoreg_db/views/patient_examination/patient_examination_create.py +63 -0
  735. endoreg_db/views/patient_examination/patient_examination_detail.py +66 -0
  736. endoreg_db/views/patient_examination/patient_examination_list.py +68 -0
  737. endoreg_db/views/patient_examination/video.py +194 -0
  738. endoreg_db/views/patient_finding/__init__.py +7 -0
  739. endoreg_db/views/patient_finding/base.py +0 -0
  740. endoreg_db/views/patient_finding/patient_finding.py +64 -0
  741. endoreg_db/views/patient_finding/patient_finding_optimized.py +259 -0
  742. endoreg_db/views/patient_finding_classification/__init__.py +5 -0
  743. endoreg_db/views/patient_finding_classification/pfc_create.py +67 -0
  744. endoreg_db/views/patient_finding_location/__init__.py +5 -0
  745. endoreg_db/views/patient_finding_location/pfl_create.py +70 -0
  746. endoreg_db/views/patient_finding_morphology/__init__.py +5 -0
  747. endoreg_db/views/patient_finding_morphology/pfm_create.py +70 -0
  748. endoreg_db/views/pdf/__init__.py +8 -0
  749. endoreg_db/views/pdf/pdf_stream.py +187 -0
  750. endoreg_db/views/pdf/reimport.py +177 -0
  751. endoreg_db/views/report/__init__.py +9 -0
  752. endoreg_db/views/report/report_list.py +112 -0
  753. endoreg_db/views/report/report_with_secure_url.py +28 -0
  754. endoreg_db/views/report/start_examination.py +7 -0
  755. endoreg_db/views/requirement/__init__.py +10 -0
  756. endoreg_db/views/requirement/evaluate.py +279 -0
  757. endoreg_db/views/requirement/lookup.py +367 -0
  758. endoreg_db/views/requirement/lookup_store.py +252 -0
  759. endoreg_db/views/requirement_lookup/lookup.py +0 -0
  760. endoreg_db/views/requirement_lookup/lookup_store.py +0 -0
  761. endoreg_db/views/stats/__init__.py +13 -0
  762. endoreg_db/views/stats/stats_views.py +229 -0
  763. endoreg_db/views/video/__init__.py +59 -0
  764. endoreg_db/views/video/correction.py +530 -0
  765. endoreg_db/views/video/reimport.py +195 -0
  766. endoreg_db/views/video/segmentation.py +274 -0
  767. endoreg_db/views/video/task_status.py +49 -0
  768. endoreg_db/views/video/timeline.py +46 -0
  769. endoreg_db/views/video/video_analyze.py +52 -0
  770. endoreg_db/views/video/video_apply_mask.py +48 -0
  771. endoreg_db/views/video/video_correction.py +21 -0
  772. endoreg_db/views/video/video_download_processed.py +58 -0
  773. endoreg_db/views/video/video_examination_viewset.py +242 -0
  774. endoreg_db/views/video/video_meta.py +29 -0
  775. endoreg_db/views/video/video_processing_history.py +24 -0
  776. endoreg_db/views/video/video_remove_frames.py +48 -0
  777. endoreg_db/views/video/video_stream.py +306 -0
  778. endoreg_db/views.py +0 -3
  779. endoreg_db-0.8.6.1.dist-info/METADATA +383 -0
  780. endoreg_db-0.8.6.1.dist-info/RECORD +793 -0
  781. {endoreg_db-0.3.6.dist-info → endoreg_db-0.8.6.1.dist-info}/WHEEL +1 -1
  782. {endoreg_db-0.3.6.dist-info → endoreg_db-0.8.6.1.dist-info/licenses}/LICENSE +674 -674
  783. endoreg_db/data/active_model/data.yaml +0 -3
  784. endoreg_db/data/label/label-set/data.yaml +0 -18
  785. endoreg_db/management/commands/_load_model_template.py +0 -41
  786. endoreg_db/management/commands/delete_all.py +0 -18
  787. endoreg_db/management/commands/delete_legacy_images.py +0 -19
  788. endoreg_db/management/commands/delete_legacy_videos.py +0 -17
  789. endoreg_db/management/commands/extract_legacy_video_frames.py +0 -18
  790. endoreg_db/management/commands/fetch_legacy_image_dataset.py +0 -32
  791. endoreg_db/management/commands/fix_auth_permission.py +0 -20
  792. endoreg_db/management/commands/import_legacy_images.py +0 -94
  793. endoreg_db/management/commands/import_legacy_videos.py +0 -76
  794. endoreg_db/management/commands/load_active_model_data.py +0 -45
  795. endoreg_db/management/commands/load_endoscopy_processor_data.py +0 -45
  796. endoreg_db/management/commands/load_g_play_data.py +0 -113
  797. endoreg_db/management/commands/load_label_data.py +0 -67
  798. endoreg_db/management/commands/load_medication_indication_data.py +0 -63
  799. endoreg_db/management/commands/load_medication_schedule_data.py +0 -55
  800. endoreg_db/migrations/0002_rawvideofile.py +0 -26
  801. endoreg_db/migrations/0003_rawvideofile_frames_required.py +0 -18
  802. endoreg_db/migrations/0004_rename_hash_rawvideofile_video_hash.py +0 -18
  803. endoreg_db/migrations/0005_ffmpegmeta_remove_videoimportmeta_center_and_more.py +0 -56
  804. endoreg_db/migrations/0006_rawvideofile_center_alter_videometa_processor.py +0 -25
  805. endoreg_db/migrations/0007_rawvideofile_processor.py +0 -19
  806. endoreg_db/migrations/0008_rename_frames_required_rawvideofile_state_frames_required.py +0 -18
  807. endoreg_db/migrations/0009_sensitivemeta_rawvideofile_sensitive_meta.py +0 -31
  808. endoreg_db/migrations/0010_rename_endoscope_serial_number_sensitivemeta_endoscope_sn.py +0 -18
  809. endoreg_db/migrations/0011_rawvideofile_state_sensitive_data_retrieved.py +0 -18
  810. endoreg_db/migrations/0012_rawvideofile_prediction_dir_and_more.py +0 -109
  811. endoreg_db/migrations/0013_rawpdffile.py +0 -31
  812. endoreg_db/migrations/0014_pdftype_alter_rawpdffile_file_pdfmeta.py +0 -38
  813. endoreg_db/migrations/0015_rename_report_processed_rawpdffile_state_report_processed_and_more.py +0 -31
  814. endoreg_db/migrations/0016_rawpdffile_state_report_processing_required.py +0 -18
  815. endoreg_db/migrations/0017_firstname_lastname_center_first_names_and_more.py +0 -37
  816. endoreg_db/migrations/0018_reportreaderflag_reportreaderconfig.py +0 -37
  817. endoreg_db/migrations/0019_pdftype_cut_off_above_lines_and_more.py +0 -42
  818. endoreg_db/migrations/0020_rename_endoscopy_info_line_pdftype_endoscope_info_line.py +0 -18
  819. endoreg_db/migrations/0021_alter_pdftype_endoscope_info_line.py +0 -19
  820. endoreg_db/migrations/0022_alter_pdftype_endoscope_info_line.py +0 -19
  821. endoreg_db/migrations/0023_ttoquestionnaire_alter_pdftype_endoscope_info_line.py +0 -59
  822. endoreg_db/migrations/0024_remove_ttoquestionnaire_infections_and_more.py +0 -27
  823. endoreg_db/migrations/0025_event_alter_rawpdffile_file_patientevent.py +0 -42
  824. endoreg_db/migrations/0026_disease_diseaseclassification_and_more.py +0 -166
  825. endoreg_db/migrations/0027_labvalue_abbreviation_labvalue_default_normal_range_and_more.py +0 -38
  826. endoreg_db/migrations/0028_alter_unit_abbreviation.py +0 -18
  827. endoreg_db/migrations/0029_medicationintaketime_and_more.py +0 -75
  828. endoreg_db/migrations/0030_medicationindicationtype_and_more.py +0 -101
  829. endoreg_db/migrations/0031_rename_adapt_to_liver_function_medication_adapt_to_age_and_more.py +0 -38
  830. endoreg_db/migrations/0032_alter_medicationschedule_therapy_duration_d.py +0 -18
  831. endoreg_db/migrations/0033_medicationindication_sources.py +0 -18
  832. endoreg_db/migrations/0034_alter_rawpdffile_file.py +0 -20
  833. endoreg_db/migrations/0035_alter_medicationindication_sources.py +0 -18
  834. endoreg_db/migrations/0036_alter_rawpdffile_file.py +0 -20
  835. endoreg_db/migrations/0037_alter_medicationindication_sources.py +0 -18
  836. endoreg_db/migrations/0038_emissionfactor_material_product_productgroup_and_more.py +0 -164
  837. endoreg_db/migrations/0039_referenceproduct_name.py +0 -19
  838. endoreg_db/migrations/0040_quizanswertype_quizquestiontype_quizquestion_and_more.py +0 -50
  839. endoreg_db/migrations/0041_gender_patientmedication_medication_indication_and_more.py +0 -40
  840. endoreg_db/migrations/0042_casetemplateruletype_casetemplaterulevalue_and_more.py +0 -74
  841. endoreg_db/migrations/0043_casetemplatetype_name_de_casetemplatetype_name_en.py +0 -23
  842. endoreg_db/migrations/0044_casetemplateruletype_name_de_and_more.py +0 -23
  843. endoreg_db/migrations/0045_casetemplaterulevalue_value_type.py +0 -19
  844. endoreg_db/migrations/0046_casetemplaterulevalue_target_field.py +0 -18
  845. endoreg_db/migrations/0047_casetemplaterule_target_model.py +0 -18
  846. endoreg_db/migrations/0048_remove_casetemplaterule_chained_rules_and_more.py +0 -22
  847. endoreg_db/migrations/0049_remove_casetemplaterule_rule_values.py +0 -17
  848. endoreg_db/migrations/0050_casetemplaterule_rule_values.py +0 -18
  849. endoreg_db/migrations/0051_remove_casetemplaterule_calling_rules_and_more.py +0 -27
  850. endoreg_db/migrations/0052_rename_case_template_type_casetemplate_template_type.py +0 -18
  851. endoreg_db/migrations/0053_patientlabsampletype_patientlabsample_and_more.py +0 -38
  852. endoreg_db/migrations/0054_multiplecategoricalvaluedistribution_and_more.py +0 -69
  853. endoreg_db/migrations/0055_remove_casetemplaterule_rule_values_and_more.py +0 -59
  854. endoreg_db/migrations/0056_datevaluedistribution_and_more.py +0 -32
  855. endoreg_db/migrations/0057_remove_datevaluedistribution_max_date_and_more.py +0 -72
  856. endoreg_db/migrations/0058_datevaluedistribution_description_and_more.py +0 -28
  857. endoreg_db/migrations/0059_casetemplaterule_rule_values.py +0 -18
  858. endoreg_db/migrations/0060_labvalue__default_date_value_distribution_and_more.py +0 -44
  859. endoreg_db/migrations/0061_remove_patientlabvalue_date_patientlabvalue_datetime.py +0 -24
  860. endoreg_db/migrations/0062_labvalue_numeric_precision.py +0 -18
  861. endoreg_db/migrations/0063_alter_labvalue_numeric_precision.py +0 -18
  862. endoreg_db/migrations/0064_casetemplaterule_extra_parameters_and_more.py +0 -23
  863. endoreg_db/migrations/0065_rename__date_value_distribution_casetemplaterule_date_value_distribution_and_more.py +0 -58
  864. endoreg_db/migrations/0066_alter_patientlabvalue_patient_and_more.py +0 -29
  865. endoreg_db/migrations/0067_alter_medicationindication_indication_type.py +0 -19
  866. endoreg_db/models/ai_model/__init__.py +0 -3
  867. endoreg_db/models/ai_model/active_model.py +0 -9
  868. endoreg_db/models/ai_model/model_meta.py +0 -24
  869. endoreg_db/models/annotation/__init__.py +0 -2
  870. endoreg_db/models/annotation/binary_classification_annotation_task.py +0 -80
  871. endoreg_db/models/annotation/image_classification.py +0 -27
  872. endoreg_db/models/center/center.py +0 -25
  873. endoreg_db/models/center/center_product.py +0 -34
  874. endoreg_db/models/center/center_resource.py +0 -19
  875. endoreg_db/models/center/center_waste.py +0 -11
  876. endoreg_db/models/data_file/__init__.py +0 -6
  877. endoreg_db/models/data_file/base_classes/__init__.py +0 -2
  878. endoreg_db/models/data_file/base_classes/abstract_frame.py +0 -51
  879. endoreg_db/models/data_file/base_classes/abstract_video.py +0 -201
  880. endoreg_db/models/data_file/frame.py +0 -45
  881. endoreg_db/models/data_file/import_classes/__init__.py +0 -32
  882. endoreg_db/models/data_file/import_classes/processing_functions/__init__.py +0 -35
  883. endoreg_db/models/data_file/import_classes/processing_functions/pdf.py +0 -28
  884. endoreg_db/models/data_file/import_classes/processing_functions/video.py +0 -260
  885. endoreg_db/models/data_file/import_classes/raw_pdf.py +0 -188
  886. endoreg_db/models/data_file/import_classes/raw_video.py +0 -343
  887. endoreg_db/models/data_file/metadata/__init__.py +0 -3
  888. endoreg_db/models/data_file/metadata/sensitive_meta.py +0 -31
  889. endoreg_db/models/data_file/metadata/video_meta.py +0 -133
  890. endoreg_db/models/data_file/report_file.py +0 -89
  891. endoreg_db/models/data_file/video/__init__.py +0 -7
  892. endoreg_db/models/data_file/video/import_meta.py +0 -25
  893. endoreg_db/models/data_file/video/video.py +0 -25
  894. endoreg_db/models/data_file/video_segment.py +0 -107
  895. endoreg_db/models/disease.py +0 -56
  896. endoreg_db/models/emission/__init__.py +0 -1
  897. endoreg_db/models/emission/emission_factor.py +0 -20
  898. endoreg_db/models/event.py +0 -22
  899. endoreg_db/models/examination/__init__.py +0 -4
  900. endoreg_db/models/examination/examination.py +0 -26
  901. endoreg_db/models/examination/examination_time.py +0 -27
  902. endoreg_db/models/examination/examination_time_type.py +0 -24
  903. endoreg_db/models/examination/examination_type.py +0 -18
  904. endoreg_db/models/hardware/__init__.py +0 -2
  905. endoreg_db/models/hardware/endoscope.py +0 -44
  906. endoreg_db/models/information_source.py +0 -29
  907. endoreg_db/models/laboratory/__init__.py +0 -1
  908. endoreg_db/models/laboratory/lab_value.py +0 -102
  909. endoreg_db/models/legacy_data/__init__.py +0 -3
  910. endoreg_db/models/legacy_data/image.py +0 -34
  911. endoreg_db/models/medication/__init__.py +0 -1
  912. endoreg_db/models/medication/medication.py +0 -148
  913. endoreg_db/models/other/distribution.py +0 -215
  914. endoreg_db/models/patient_examination/__init__.py +0 -35
  915. endoreg_db/models/permissions/__init__.py +0 -44
  916. endoreg_db/models/persons/__init__.py +0 -7
  917. endoreg_db/models/persons/examiner/__init__.py +0 -2
  918. endoreg_db/models/persons/examiner/examiner.py +0 -16
  919. endoreg_db/models/persons/examiner/examiner_type.py +0 -2
  920. endoreg_db/models/persons/patient/__init__.py +0 -8
  921. endoreg_db/models/persons/patient/case/case.py +0 -30
  922. endoreg_db/models/persons/patient/patient.py +0 -216
  923. endoreg_db/models/persons/patient/patient_disease.py +0 -16
  924. endoreg_db/models/persons/patient/patient_event.py +0 -22
  925. endoreg_db/models/persons/patient/patient_lab_sample.py +0 -106
  926. endoreg_db/models/persons/patient/patient_medication.py +0 -44
  927. endoreg_db/models/persons/patient/patient_medication_schedule.py +0 -28
  928. endoreg_db/models/persons/portal_user_information.py +0 -27
  929. endoreg_db/models/prediction/__init__.py +0 -2
  930. endoreg_db/models/prediction/image_classification.py +0 -37
  931. endoreg_db/models/prediction/video_prediction_meta.py +0 -244
  932. endoreg_db/models/product/__init__.py +0 -5
  933. endoreg_db/models/product/product.py +0 -97
  934. endoreg_db/models/product/product_group.py +0 -19
  935. endoreg_db/models/product/product_material.py +0 -24
  936. endoreg_db/models/questionnaires/__init__.py +0 -114
  937. endoreg_db/models/quiz/__init__.py +0 -2
  938. endoreg_db/models/quiz/quiz_answer.py +0 -41
  939. endoreg_db/models/quiz/quiz_question.py +0 -54
  940. endoreg_db/models/report_reader/__init__.py +0 -2
  941. endoreg_db/models/report_reader/report_reader_config.py +0 -53
  942. endoreg_db/models/rules/__init__.py +0 -5
  943. endoreg_db/queries/get/__init__.py +0 -6
  944. endoreg_db/queries/get/center.py +0 -42
  945. endoreg_db/queries/get/model.py +0 -13
  946. endoreg_db/queries/get/patient.py +0 -14
  947. endoreg_db/queries/get/patient_examination.py +0 -20
  948. endoreg_db/queries/get/report_file.py +0 -33
  949. endoreg_db/queries/get/video.py +0 -31
  950. endoreg_db/serializers/ai_model.py +0 -19
  951. endoreg_db/serializers/annotation.py +0 -17
  952. endoreg_db/serializers/center.py +0 -11
  953. endoreg_db/serializers/examination.py +0 -33
  954. endoreg_db/serializers/frame.py +0 -13
  955. endoreg_db/serializers/hardware.py +0 -21
  956. endoreg_db/serializers/label.py +0 -22
  957. endoreg_db/serializers/patient.py +0 -10
  958. endoreg_db/serializers/prediction.py +0 -15
  959. endoreg_db/serializers/report_file.py +0 -7
  960. endoreg_db/serializers/video.py +0 -27
  961. endoreg_db/tests.py +0 -3
  962. endoreg_db/utils/legacy_ocr.py +0 -201
  963. endoreg_db/utils/video_metadata.py +0 -87
  964. endoreg_db-0.3.6.dist-info/METADATA +0 -33
  965. endoreg_db-0.3.6.dist-info/RECORD +0 -357
  966. /endoreg_db/{models/persons/patient/case/__init__.py → api/serializers/finding_descriptions.py} +0 -0
  967. /endoreg_db/{queries/get/annotation.py → api/views/finding_descriptions.py} +0 -0
  968. /endoreg_db/{queries/get/prediction.py → config/__init__.py} +0 -0
  969. /endoreg_db/{queries/get/video_import_meta.py → data/case_template/rule_value/.init} +0 -0
  970. /endoreg_db/{queries/get/video_prediction_meta.py → data/distribution/multiple_categorical/.init} +0 -0
@@ -0,0 +1,835 @@
1
+ import os
2
+ import subprocess
3
+ import json
4
+ import logging
5
+ from functools import lru_cache
6
+ from pathlib import Path
7
+ from typing import List, Dict, Optional, Tuple
8
+ import cv2
9
+ from tqdm import tqdm
10
+ import shutil
11
+
12
+ logger = logging.getLogger("ffmpeg_wrapper")
13
+
14
+ # Global hardware acceleration cache
15
+ _nvenc_available = None
16
+ _preferred_encoder = None
17
+
18
+
19
+ @lru_cache(maxsize=1)
20
+ def _resolve_ffmpeg_executable() -> Optional[str]:
21
+ """Locate the ffmpeg executable using multiple discovery strategies."""
22
+ # 1) Explicit overrides via env vars
23
+ env_candidates = [
24
+ os.environ.get("FFMPEG_EXECUTABLE"),
25
+ os.environ.get("FFMPEG_BINARY"),
26
+ os.environ.get("FFMPEG_PATH"),
27
+ ]
28
+
29
+ # 2) Django settings overrides (if Django is configured)
30
+ try:
31
+ from django.conf import settings # type: ignore
32
+
33
+ env_candidates.extend(
34
+ getattr(settings, attr)
35
+ for attr in ("FFMPEG_EXECUTABLE", "FFMPEG_BINARY", "FFMPEG_PATH")
36
+ if hasattr(settings, attr)
37
+ )
38
+ except Exception:
39
+ # Django might not be configured for every consumer
40
+ pass
41
+
42
+ # Normalize and verify explicit candidates
43
+ for candidate in env_candidates:
44
+ if not candidate:
45
+ continue
46
+ candidate_path = Path(candidate)
47
+ if candidate_path.is_dir():
48
+ candidate_path = candidate_path / "ffmpeg"
49
+ if candidate_path.exists() and os.access(candidate_path, os.X_OK):
50
+ logger.debug("Using ffmpeg executable override at %s", candidate_path)
51
+ return str(candidate_path)
52
+
53
+ # 3) PATH lookup (shutil.which)
54
+ via_path = shutil.which("ffmpeg")
55
+ if via_path:
56
+ return via_path
57
+
58
+ # 4) Common fallback locations (useful for Nix-based environments)
59
+ nix_store = Path("/nix/store")
60
+ if nix_store.exists():
61
+ patterns = (
62
+ "*-ffmpeg-*/bin/ffmpeg",
63
+ "*-ffmpeg-headless-*/bin/ffmpeg",
64
+ "*-ffmpeg-headless*/bin/ffmpeg",
65
+ )
66
+ for pattern in patterns:
67
+ matches = sorted(nix_store.glob(pattern))
68
+ if matches:
69
+ logger.debug("Discovered ffmpeg in nix store at %s", matches[-1])
70
+ return str(matches[-1])
71
+
72
+ # 5) Final fallback to standard Unix locations
73
+ for fallback in (Path("/usr/bin/ffmpeg"), Path("/usr/local/bin/ffmpeg")):
74
+ if fallback.exists() and os.access(fallback, os.X_OK):
75
+ return str(fallback)
76
+
77
+ return None
78
+
79
+ def _detect_nvenc_support() -> bool:
80
+ """
81
+ Detect if NVIDIA NVENC hardware acceleration is available.
82
+
83
+ Returns:
84
+ True if NVENC is available, False otherwise
85
+ """
86
+ try:
87
+ # Test NVENC availability with a minimal command (minimum size for NVENC)
88
+ cmd = [
89
+ 'ffmpeg', '-f', 'lavfi', '-i', 'testsrc=duration=1:size=256x256:rate=1',
90
+ '-c:v', 'h264_nvenc', '-preset', 'p1', '-f', 'null', '-'
91
+ ]
92
+
93
+ result = subprocess.run(
94
+ cmd,
95
+ capture_output=True,
96
+ text=True,
97
+ timeout=15,
98
+ check=False
99
+ )
100
+
101
+ if result.returncode == 0:
102
+ logger.debug("NVENC h264 encoding test successful")
103
+ return True
104
+ else:
105
+ logger.debug(f"NVENC test failed: {result.stderr}")
106
+ return False
107
+
108
+ except (subprocess.TimeoutExpired, FileNotFoundError) as e:
109
+ logger.debug(f"NVENC detection failed: {e}")
110
+ return False
111
+ except Exception as e:
112
+ logger.warning(f"Unexpected error during NVENC detection: {e}")
113
+ return False
114
+
115
+ def _get_preferred_encoder() -> Dict[str, str]:
116
+ """
117
+ Get the preferred video encoder configuration based on available hardware.
118
+
119
+ Returns:
120
+ Dictionary with encoder configuration
121
+ """
122
+ global _nvenc_available, _preferred_encoder
123
+
124
+ if _nvenc_available is None:
125
+ _nvenc_available = _detect_nvenc_support()
126
+
127
+ if _preferred_encoder is None:
128
+ if _nvenc_available:
129
+ _preferred_encoder = {
130
+ 'name': 'h264_nvenc',
131
+ 'preset_param': '-preset',
132
+ 'preset_value': 'p4', # Medium quality/speed for NVENC
133
+ 'quality_param': '-cq',
134
+ 'quality_value': '20', # NVENC CQ mode
135
+ 'type': 'nvenc',
136
+ 'fallback_preset': 'p1' # Fastest NVENC preset for fallback
137
+ }
138
+ logger.info("Hardware acceleration: NVENC available")
139
+ else:
140
+ _preferred_encoder = {
141
+ 'name': 'libx264',
142
+ 'preset_param': '-preset',
143
+ 'preset_value': 'medium', # CPU preset
144
+ 'quality_param': '-crf',
145
+ 'quality_value': '23', # CPU CRF mode
146
+ 'type': 'cpu',
147
+ 'fallback_preset': 'ultrafast' # Fastest CPU preset for fallback
148
+ }
149
+ logger.info("Hardware acceleration: NVENC not available, using CPU")
150
+
151
+ return _preferred_encoder
152
+
153
+ def _build_encoder_args(quality_mode: str = 'balanced',
154
+ fallback: bool = False,
155
+ custom_crf: Optional[int] = None) -> Tuple[List[str], str]:
156
+ """
157
+ Build encoder command arguments based on available hardware and quality requirements.
158
+
159
+ Args:
160
+ quality_mode: 'fast', 'balanced', or 'quality'
161
+ fallback: Whether to use fallback settings for compatibility
162
+ custom_crf: Override quality setting (for backward compatibility)
163
+
164
+ Returns:
165
+ Tuple of (encoder_args, encoder_type)
166
+ """
167
+ encoder = _get_preferred_encoder()
168
+
169
+ if encoder['type'] == 'nvenc':
170
+ # NVIDIA NVENC configuration
171
+ if fallback:
172
+ preset = encoder['fallback_preset'] # p1 - fastest
173
+ quality = '28' # Lower quality for speed
174
+ elif quality_mode == 'fast':
175
+ preset = 'p2' # Faster preset
176
+ quality = '25'
177
+ elif quality_mode == 'quality':
178
+ preset = 'p6' # Higher quality preset
179
+ quality = '18'
180
+ else: # balanced
181
+ preset = encoder['preset_value'] # p4
182
+ quality = encoder['quality_value'] # 20
183
+
184
+ # Override with custom CRF if provided (for backward compatibility)
185
+ if custom_crf is not None:
186
+ quality = str(custom_crf)
187
+
188
+ return [
189
+ '-c:v', encoder['name'],
190
+ encoder['preset_param'], preset,
191
+ encoder['quality_param'], quality,
192
+ '-gpu', '0', # Use first GPU
193
+ '-rc', 'vbr', # Variable bitrate
194
+ '-profile:v', 'high'
195
+ ], encoder['type']
196
+ else:
197
+ # CPU libx264 configuration
198
+ if fallback:
199
+ preset = encoder['fallback_preset'] # ultrafast
200
+ quality = '28' # Lower quality for speed
201
+ elif quality_mode == 'fast':
202
+ preset = 'faster'
203
+ quality = '20'
204
+ elif quality_mode == 'quality':
205
+ preset = 'slow'
206
+ quality = '18'
207
+ else: # balanced
208
+ preset = encoder['preset_value'] # medium
209
+ quality = encoder['quality_value'] # 23
210
+
211
+ # Override with custom CRF if provided (for backward compatibility)
212
+ if custom_crf is not None:
213
+ quality = str(custom_crf)
214
+
215
+ return [
216
+ '-c:v', encoder['name'],
217
+ encoder['preset_param'], preset,
218
+ encoder['quality_param'], quality,
219
+ '-profile:v', 'high'
220
+ ], encoder['type']
221
+
222
+ def is_ffmpeg_available() -> bool:
223
+ """
224
+ Checks whether the FFmpeg executable is available in the system's PATH.
225
+
226
+ Returns:
227
+ True if FFmpeg is found in the PATH; otherwise, False.
228
+ """
229
+ return _resolve_ffmpeg_executable() is not None
230
+
231
+ def check_ffmpeg_availability():
232
+ """
233
+ Verifies that FFmpeg is installed and available in the system's PATH.
234
+
235
+ Raises:
236
+ FileNotFoundError: If FFmpeg is not found.
237
+
238
+ Returns:
239
+ True if FFmpeg is available.
240
+ """
241
+ if not is_ffmpeg_available():
242
+ error_msg = "FFmpeg is not available. Please install it and ensure it's in your PATH."
243
+ logger.error(error_msg)
244
+ raise FileNotFoundError(error_msg)
245
+ # logger.info("FFmpeg is available.") # Caller can log if needed
246
+ return True
247
+
248
+ def get_stream_info(file_path: Path) -> Optional[Dict]:
249
+ """
250
+ Retrieves video stream information from a file using ffprobe.
251
+
252
+ Runs ffprobe to extract stream metadata in JSON format from the specified video file. Returns a dictionary with stream information, or None if the file does not exist or if an error occurs during execution or parsing.
253
+ """
254
+ if not file_path.exists():
255
+ logger.error("File not found for ffprobe: %s", file_path)
256
+ return None
257
+
258
+ command = [
259
+ "ffprobe",
260
+ "-v", "quiet",
261
+ "-print_format", "json",
262
+ "-show_streams",
263
+ str(file_path),
264
+ ]
265
+ try:
266
+ result = subprocess.run(command, capture_output=True, text=True, check=True)
267
+ return json.loads(result.stdout)
268
+ except subprocess.CalledProcessError as e:
269
+ logger.error("ffprobe command failed for %s: %s\n%s", file_path, e, e.stderr)
270
+ return None
271
+ except json.JSONDecodeError as e:
272
+ logger.error("Failed to parse ffprobe JSON output for %s: %s", file_path, e)
273
+ return None
274
+ except Exception as e:
275
+ logger.error("Error running ffprobe for %s: %s", file_path, e, exc_info=True)
276
+ return None
277
+
278
+
279
+ def assemble_video_from_frames( # Renamed from assemble_video
280
+ frame_paths: List[Path],
281
+ output_path: Path,
282
+ fps: float,
283
+ width: Optional[int] = None,
284
+ height: Optional[int] = None,
285
+ ) -> Optional[Path]:
286
+ """
287
+ Assembles a video from a list of frame image paths using cv2.VideoWriter.
288
+ Determines dimensions from the first frame if not provided.
289
+ """
290
+ if not frame_paths:
291
+ logger.error("No frame paths provided for video assembly.")
292
+ return None
293
+
294
+ if width is None or height is None:
295
+ try:
296
+ first_frame = cv2.imread(str(frame_paths[0]))
297
+ if first_frame is None:
298
+ raise IOError(f"Could not read first frame: {frame_paths[0]}")
299
+ height, width, _ = first_frame.shape
300
+ logger.info("Determined video dimensions from first frame: %dx%d", width, height)
301
+ except Exception as e:
302
+ logger.error("Error reading first frame to determine dimensions: %s", e, exc_info=True)
303
+ return None
304
+
305
+ fourcc = cv2.VideoWriter_fourcc(*"mp4v") # type: ignore
306
+ output_path.parent.mkdir(parents=True, exist_ok=True)
307
+ video_writer = cv2.VideoWriter(str(output_path), fourcc, fps, (width, height))
308
+
309
+ if not video_writer.isOpened():
310
+ logger.error("Could not open video writer for path: %s", output_path)
311
+ return None
312
+
313
+ logger.info("Assembling video %s from %d frames...", output_path.name, len(frame_paths))
314
+ try:
315
+ for frame_path in tqdm(frame_paths, desc=f"Assembling {output_path.name}"):
316
+ frame = cv2.imread(str(frame_path))
317
+ if frame is None:
318
+ logger.warning("Could not read frame %s, skipping.", frame_path)
319
+ continue
320
+ # Ensure frame dimensions match - resize if necessary (or log error)
321
+ if frame.shape[1] != width or frame.shape[0] != height:
322
+ logger.warning(f"Frame {frame_path} has dimensions {frame.shape[1]}x{frame.shape[0]}, expected {width}x{height}. Resizing.")
323
+ frame = cv2.resize(frame, (width, height))
324
+ video_writer.write(frame)
325
+ finally:
326
+ video_writer.release()
327
+ logger.info("Finished assembling video: %s", output_path)
328
+
329
+ return output_path
330
+
331
+
332
+ def transcode_video(
333
+ input_path: Path,
334
+ output_path: Path,
335
+ codec: str = "auto", # Changed default to "auto" for automatic selection
336
+ crf: Optional[int] = None, # Will be determined automatically if None
337
+ preset: str = "auto", # Changed default to "auto" for automatic selection
338
+ audio_codec: str = "aac",
339
+ audio_bitrate: str = "128k",
340
+ extra_args: Optional[List[str]] = None,
341
+ quality_mode: str = "balanced", # New parameter: 'fast', 'balanced', 'quality'
342
+ force_cpu: bool = False, # New parameter to force CPU encoding
343
+ ) -> Optional[Path]:
344
+ """
345
+ Transcodes a video file using FFmpeg with automatic hardware acceleration.
346
+
347
+ Args:
348
+ input_path: Source video file path
349
+ output_path: Output video file path
350
+ codec: Video codec ('auto' for automatic selection, 'libx264', 'h264_nvenc')
351
+ crf: Constant Rate Factor (None for automatic selection)
352
+ preset: Encoder preset ('auto' for automatic selection)
353
+ audio_codec: Audio codec
354
+ audio_bitrate: Audio bitrate
355
+ extra_args: Additional FFmpeg arguments
356
+ quality_mode: Quality mode ('fast', 'balanced', 'quality')
357
+ force_cpu: Force CPU encoding even if NVENC is available
358
+
359
+ Returns:
360
+ Path to transcoded video or None if failed
361
+ """
362
+ if not input_path.exists():
363
+ logger.error("Input file not found for transcoding: %s", input_path)
364
+ return None
365
+
366
+ output_path.parent.mkdir(parents=True, exist_ok=True)
367
+
368
+ # Determine encoder configuration
369
+ if codec == "auto" or preset == "auto":
370
+ if force_cpu:
371
+ # Force CPU encoding
372
+ encoder_args, encoder_type = _build_encoder_args(quality_mode, fallback=False, custom_crf=crf)
373
+ # Override to use CPU encoder
374
+ encoder_args[1] = 'libx264' # Replace encoder name
375
+ encoder_args[3] = 'medium' if preset == "auto" else preset # Replace preset
376
+ if crf is not None:
377
+ encoder_args[5] = str(crf) # Replace quality value
378
+ else:
379
+ # Use automatic hardware detection
380
+ encoder_args, encoder_type = _build_encoder_args(quality_mode, fallback=False, custom_crf=crf)
381
+ else:
382
+ # Manual codec/preset specification (backward compatibility)
383
+ encoder_args = [
384
+ '-c:v', codec,
385
+ '-preset', preset,
386
+ '-crf' if codec == 'libx264' else '-cq', str(crf if crf is not None else 23),
387
+ ]
388
+ encoder_type = 'nvenc' if 'nvenc' in codec else 'cpu'
389
+
390
+ # Build complete command
391
+ command = [
392
+ "ffmpeg",
393
+ "-i", str(input_path),
394
+ *encoder_args,
395
+ "-c:a", audio_codec,
396
+ "-b:a", audio_bitrate,
397
+ "-y", # Overwrite output file if it exists
398
+ ]
399
+
400
+ if extra_args:
401
+ command.extend(extra_args)
402
+ command.append(str(output_path))
403
+
404
+ logger.info("Starting transcoding: %s -> %s (using %s)",
405
+ input_path.name, output_path.name, encoder_type)
406
+ logger.debug("FFmpeg command: %s", " ".join(command))
407
+
408
+ try:
409
+ process = subprocess.Popen(command, stderr=subprocess.PIPE, text=True, universal_newlines=True)
410
+
411
+ # Progress reporting and error handling
412
+ stderr_output = ""
413
+ if process.stderr:
414
+ for line in process.stderr:
415
+ stderr_output += line
416
+
417
+ process.wait()
418
+
419
+ if process.returncode == 0:
420
+ logger.info("Transcoding finished successfully: %s", output_path)
421
+ return output_path
422
+ else:
423
+ logger.error("FFmpeg transcoding failed for %s with return code %d.",
424
+ input_path.name, process.returncode)
425
+ logger.error("FFmpeg stderr:\n%s", stderr_output)
426
+
427
+ # Try fallback to CPU if NVENC failed
428
+ if encoder_type == 'nvenc' and not force_cpu:
429
+ logger.warning("NVENC transcoding failed, trying CPU fallback...")
430
+ return _transcode_video_fallback(
431
+ input_path, output_path, audio_codec, audio_bitrate,
432
+ extra_args, quality_mode, crf
433
+ )
434
+
435
+ # Clean up potentially corrupted output file
436
+ if output_path.exists():
437
+ try:
438
+ output_path.unlink()
439
+ except OSError as e:
440
+ logger.error("Failed to delete incomplete output file %s: %s", output_path, e)
441
+ return None
442
+
443
+ except FileNotFoundError:
444
+ logger.error("ffmpeg command not found. Ensure FFmpeg is installed and in the system's PATH.")
445
+ return None
446
+ except Exception as e:
447
+ logger.error("Error during transcoding of %s: %s", input_path.name, e, exc_info=True)
448
+ return None
449
+
450
+ def _transcode_video_fallback(
451
+ input_path: Path,
452
+ output_path: Path,
453
+ audio_codec: str,
454
+ audio_bitrate: str,
455
+ extra_args: Optional[List[str]],
456
+ quality_mode: str,
457
+ custom_crf: Optional[int]
458
+ ) -> Optional[Path]:
459
+ """
460
+ Fallback transcoding using CPU encoding.
461
+
462
+ Args:
463
+ input_path: Source video file path
464
+ output_path: Output video file path
465
+ audio_codec: Audio codec
466
+ audio_bitrate: Audio bitrate
467
+ extra_args: Additional FFmpeg arguments
468
+ quality_mode: Quality mode
469
+ custom_crf: Custom CRF value
470
+
471
+ Returns:
472
+ Path to transcoded video or None if failed
473
+ """
474
+ try:
475
+ # Build CPU encoder arguments
476
+ encoder_args, _ = _build_encoder_args(quality_mode, fallback=True, custom_crf=custom_crf)
477
+ # Force CPU encoder
478
+ encoder_args[1] = 'libx264'
479
+
480
+ command = [
481
+ "ffmpeg",
482
+ "-i", str(input_path),
483
+ *encoder_args,
484
+ "-c:a", audio_codec,
485
+ "-b:a", audio_bitrate,
486
+ "-y",
487
+ ]
488
+
489
+ if extra_args:
490
+ command.extend(extra_args)
491
+ command.append(str(output_path))
492
+
493
+ logger.info("CPU fallback transcoding: %s -> %s", input_path.name, output_path.name)
494
+ logger.debug("Fallback FFmpeg command: %s", " ".join(command))
495
+
496
+ process = subprocess.Popen(command, stderr=subprocess.PIPE, text=True, universal_newlines=True)
497
+ stderr_output = ""
498
+ if process.stderr:
499
+ for line in process.stderr:
500
+ stderr_output += line
501
+
502
+ process.wait()
503
+
504
+ if process.returncode == 0:
505
+ logger.info("CPU fallback transcoding successful: %s", output_path)
506
+ return output_path
507
+ else:
508
+ logger.error("CPU fallback transcoding also failed for %s", input_path.name)
509
+ logger.error("Fallback stderr:\n%s", stderr_output)
510
+ return None
511
+
512
+ except Exception as e:
513
+ logger.error("Error during CPU fallback transcoding: %s", e, exc_info=True)
514
+ return None
515
+
516
+ logger.info("Starting transcoding: %s -> %s", input_path.name, output_path.name)
517
+ logger.debug("FFmpeg command: %s", " ".join(command))
518
+
519
+ try:
520
+ process = subprocess.Popen(command, stderr=subprocess.PIPE, text=True, universal_newlines=True)
521
+
522
+ # Optional: Progress reporting (can be complex to parse ffmpeg output reliably)
523
+ # For simplicity, just wait and check the return code
524
+ stderr_output = ""
525
+ if process.stderr:
526
+ for line in process.stderr:
527
+ stderr_output += line
528
+ # Simple progress indication or detailed logging
529
+ # logger.debug(f"ffmpeg: {line.strip()}")
530
+
531
+ process.wait()
532
+
533
+ if process.returncode == 0:
534
+ logger.info("Transcoding finished successfully: %s", output_path)
535
+ return output_path
536
+ else:
537
+ logger.error("FFmpeg transcoding failed for %s with return code %d.", input_path.name, process.returncode)
538
+ logger.error("FFmpeg stderr:\n%s", stderr_output)
539
+ # Clean up potentially corrupted output file
540
+ if output_path.exists():
541
+ try:
542
+ output_path.unlink()
543
+ except OSError as e:
544
+ logger.error("Failed to delete incomplete output file %s: %s", output_path, e)
545
+ return None
546
+
547
+ except FileNotFoundError:
548
+ logger.error("ffmpeg command not found. Ensure FFmpeg is installed and in the system's PATH.")
549
+ return None
550
+ except Exception as e:
551
+ logger.error("Error during transcoding of %s: %s", input_path.name, e, exc_info=True)
552
+ return None
553
+
554
+ def transcode_videofile_if_required(
555
+ input_path: Path,
556
+ output_path: Path,
557
+ required_codec: str = "h264",
558
+ required_pixel_format: str = "yuv420p", # Changed default from yuvj420p
559
+ **transcode_options # Pass other options to transcode_video
560
+ ) -> Optional[Path]:
561
+ """
562
+ Checks if a video needs transcoding based on codec and pixel format,
563
+ and transcodes it using transcode_video if necessary.
564
+ Uses yuv420p with full color range (pc/jpeg) as the target format.
565
+ Returns the path to the compliant video (original or transcoded).
566
+ """
567
+ stream_info = get_stream_info(input_path)
568
+ if not stream_info or "streams" not in stream_info:
569
+ logger.error("Could not get stream info for %s to check if transcoding is required.", input_path)
570
+ return None
571
+
572
+ video_stream = next((s for s in stream_info["streams"] if s.get("codec_type") == "video"), None)
573
+
574
+ if not video_stream:
575
+ logger.error("No video stream found in %s.", input_path)
576
+ return None
577
+
578
+ codec_name = video_stream.get("codec_name")
579
+ pixel_format = video_stream.get("pix_fmt")
580
+ # Check color range as well, default is usually 'tv' (limited)
581
+ color_range = video_stream.get("color_range", "tv") # Default to tv if not specified
582
+
583
+ needs_transcoding = False
584
+ transcode_reason = []
585
+ if codec_name != required_codec:
586
+ reason = f"Codec mismatch ({codec_name} != {required_codec})"
587
+ logger.info("%s for %s. Transcoding required.", reason, input_path.name)
588
+ transcode_reason.append(reason)
589
+ needs_transcoding = True
590
+ # Check both pixel format and color range for yuv420p
591
+ if pixel_format != required_pixel_format or (pixel_format == "yuv420p" and color_range != "pc"):
592
+ reason = f"Pixel format/color range mismatch (pix_fmt: {pixel_format}, color_range: {color_range} != {required_pixel_format} with color_range=pc)"
593
+ logger.info("%s for %s. Transcoding required.", reason, input_path.name)
594
+ transcode_reason.append(reason)
595
+ needs_transcoding = True
596
+
597
+ if needs_transcoding:
598
+ logger.info("Transcoding %s to %s due to: %s", input_path.name, output_path.name, "; ".join(transcode_reason))
599
+ # Ensure codec and pixel format are set in options if not already present
600
+ transcode_options.setdefault('codec', 'libx264' if required_codec == 'h264' else required_codec)
601
+ transcode_options.setdefault('extra_args', [])
602
+
603
+ # Ensure pixel format and color range are correctly set in extra_args
604
+ extra_args = transcode_options['extra_args']
605
+ if '-pix_fmt' not in extra_args:
606
+ extra_args.extend(['-pix_fmt', required_pixel_format])
607
+ else:
608
+ # If pix_fmt is already set, ensure it's the required one
609
+ try:
610
+ pix_fmt_index = extra_args.index('-pix_fmt')
611
+ if extra_args[pix_fmt_index + 1] != required_pixel_format:
612
+ logger.warning("Overriding existing -pix_fmt '%s' with '%s'", extra_args[pix_fmt_index + 1], required_pixel_format)
613
+ extra_args[pix_fmt_index + 1] = required_pixel_format
614
+ except (ValueError, IndexError):
615
+ # Should not happen if '-pix_fmt' is in extra_args, but handle defensively
616
+ logger.error("Error processing existing -pix_fmt argument. Appending required format.")
617
+ extra_args.extend(['-pix_fmt', required_pixel_format])
618
+
619
+
620
+ if '-color_range' not in extra_args:
621
+ # Add color range 'pc' (which corresponds to 2 or 'jpeg') for yuv420p
622
+ extra_args.extend(['-color_range', 'pc'])
623
+ else:
624
+ # If color_range is already set, ensure it's 'pc'
625
+ try:
626
+ color_range_index = extra_args.index('-color_range')
627
+ if extra_args[color_range_index + 1] != 'pc':
628
+ logger.warning("Overriding existing -color_range '%s' with 'pc'", extra_args[color_range_index + 1])
629
+ extra_args[color_range_index + 1] = 'pc'
630
+ except (ValueError, IndexError):
631
+ logger.error("Error processing existing -color_range argument. Appending 'pc'.")
632
+ extra_args.extend(['-color_range', 'pc'])
633
+
634
+
635
+ return transcode_video(input_path, output_path, **transcode_options)
636
+ else:
637
+ logger.info("Video %s already meets requirements (%s, %s, color_range=pc). No transcoding needed.", input_path.name, required_codec, required_pixel_format)
638
+ # If no transcoding is needed, should we copy/link or just return the original path?
639
+ # For simplicity, let's assume the caller handles the file location.
640
+ # If the output_path is different, we might need to copy.
641
+ if input_path != output_path:
642
+ # Example: copy file if output path is different
643
+ try:
644
+ output_path.parent.mkdir(parents=True, exist_ok=True)
645
+ shutil.copy2(input_path, output_path)
646
+ logger.info("Copied %s to %s as it met requirements.", input_path.name, output_path.name)
647
+ return output_path
648
+ except Exception as e:
649
+ logger.error("Failed to copy %s to %s: %s", input_path.name, output_path.name, e)
650
+ return None
651
+ return input_path # Return original path if no copy needed
652
+
653
+ def extract_frames(
654
+ video_path: Path,
655
+ output_dir: Path,
656
+ quality: int,
657
+ ext: str = "jpg",
658
+ fps: Optional[float] = None
659
+ ) -> List[Path]:
660
+ """
661
+ Extracts frames from a video file using FFmpeg.
662
+
663
+ Args:
664
+ video_path: Path to the input video file.
665
+ output_dir: Directory to save the extracted frames.
666
+ quality: Quality factor for JPEG extraction (1-31, lower is better).
667
+ ext: Output frame image extension (e.g., 'jpg', 'png').
668
+ fps: Optional frames per second to extract. If None, extracts all frames.
669
+
670
+ Returns:
671
+ A list of Path objects for the extracted frames.
672
+ """
673
+ # Resolve ffmpeg executable with multiple fallbacks
674
+ ffmpeg_executable = _resolve_ffmpeg_executable()
675
+ if not ffmpeg_executable:
676
+ error_msg = "ffmpeg command not found. Ensure FFmpeg is installed and in the system's PATH."
677
+ logger.error(error_msg)
678
+ raise FileNotFoundError(error_msg)
679
+
680
+ output_dir.mkdir(parents=True, exist_ok=True)
681
+ output_pattern = output_dir / f"frame_%07d.{ext}"
682
+
683
+ cmd = [
684
+ ffmpeg_executable, # Use the found executable path
685
+ "-i", str(video_path),
686
+ "-qscale:v", str(quality), # Video quality scale
687
+ ]
688
+
689
+ if fps is not None:
690
+ cmd.extend(["-vf", f"fps={fps}"])
691
+
692
+ cmd.append(str(output_pattern))
693
+
694
+ logger.info("Running FFmpeg command: %s", " ".join(cmd))
695
+ try:
696
+ # Use subprocess.run for better error handling
697
+ result = subprocess.run(cmd, check=True, capture_output=True, text=True)
698
+ logger.debug("FFmpeg stdout:\n%s", result.stdout)
699
+ logger.debug("FFmpeg stderr:\n%s", result.stderr)
700
+ logger.info("FFmpeg frame extraction completed successfully.")
701
+ except FileNotFoundError as exc:
702
+ # This might be redundant now but kept for safety
703
+ error_msg = f"ffmpeg command not found at '{ffmpeg_executable}'. Ensure FFmpeg is installed and in the system's PATH."
704
+ logger.error(error_msg)
705
+ raise FileNotFoundError(error_msg) from exc
706
+ except subprocess.CalledProcessError as e:
707
+ logger.error("FFmpeg command failed with exit code %d.", e.returncode)
708
+ logger.error("FFmpeg stderr:\n%s", e.stderr)
709
+ logger.error("FFmpeg stdout:\n%s", e.stdout)
710
+ # Return empty list on error as frames were likely not created correctly
711
+ return []
712
+ except Exception as e:
713
+ logger.error("An unexpected error occurred during FFmpeg execution: %s", e, exc_info=True)
714
+ return []
715
+
716
+
717
+ # Collect paths of extracted frames
718
+ extracted_files = sorted(output_dir.glob(f"frame_*.{ext}"))
719
+ return extracted_files
720
+
721
+ def extract_frame_range(
722
+ video_path: Path,
723
+ output_dir: Path,
724
+ start_frame: int,
725
+ end_frame: int, # Exclusive end frame number
726
+ quality: int,
727
+ ext: str = "jpg",
728
+ ) -> List[Path]:
729
+ """
730
+ Extracts a specific range of frames from a video using FFmpeg.
731
+
732
+ Frames from start_frame (inclusive) to end_frame (exclusive) are saved as images
733
+ in the output directory, following the naming pattern 'frame_%07d.ext'. The
734
+ function ensures only the requested frames are returned, and cleans up partial
735
+ results on failure.
736
+
737
+ Args:
738
+ video_path: Path to the input video file.
739
+ output_dir: Directory where extracted frames will be saved.
740
+ start_frame: Index of the first frame to extract (inclusive, 0-based).
741
+ end_frame: Index at which to stop extraction (exclusive, 0-based).
742
+ quality: JPEG quality factor (1-31, lower is better).
743
+ ext: File extension for output images (e.g., 'jpg', 'png').
744
+
745
+ Returns:
746
+ List of Paths to the extracted frame image files within the specified range.
747
+
748
+ Raises:
749
+ FileNotFoundError: If the FFmpeg executable is not found.
750
+ ValueError: If start_frame is greater than or equal to end_frame.
751
+ RuntimeError: If FFmpeg fails to extract the requested frames.
752
+ """
753
+ if start_frame >= end_frame:
754
+ logger.warning("extract_frame_range called with start_frame (%d) >= end_frame (%d). No frames to extract.", start_frame, end_frame)
755
+ return []
756
+
757
+ ffmpeg_executable = _resolve_ffmpeg_executable()
758
+ if not ffmpeg_executable:
759
+ error_msg = "ffmpeg command not found. Ensure FFmpeg is installed and in the system's PATH."
760
+ logger.error(error_msg)
761
+ raise FileNotFoundError(error_msg)
762
+
763
+ output_dir.mkdir(parents=True, exist_ok=True)
764
+ # Use a consistent naming convention, matching extract_frames
765
+ output_pattern = output_dir / f"frame_%07d.{ext}"
766
+
767
+ # Use select filter for precise frame range extraction
768
+ # 'select' uses 0-based indexing 'n'
769
+ # We want frames where start_frame <= n < end_frame
770
+ select_filter = f"select='between(n,{start_frame},{end_frame-1})'"
771
+
772
+ cmd = [
773
+ ffmpeg_executable,
774
+ "-i", str(video_path),
775
+ "-vf", select_filter,
776
+ "-vsync", "vfr", # Variable frame rate sync to handle selected frames
777
+ "-qscale:v", str(quality),
778
+ "-copyts", # Attempt to copy timestamps if needed, might not be accurate with select
779
+ str(output_pattern),
780
+ ]
781
+
782
+ logger.info("Running FFmpeg command for frame range extraction: %s", " ".join(cmd))
783
+ try:
784
+ result = subprocess.run(cmd, check=True, capture_output=True, text=True)
785
+ logger.debug("FFmpeg stdout:\n%s", result.stdout)
786
+ logger.debug("FFmpeg stderr:\n%s", result.stderr)
787
+ logger.info("FFmpeg frame range extraction completed successfully.")
788
+ except FileNotFoundError as exc:
789
+ error_msg = f"ffmpeg command not found at '{ffmpeg_executable}'. Ensure FFmpeg is installed and in the system's PATH."
790
+ logger.error(error_msg)
791
+ raise FileNotFoundError(error_msg) from exc
792
+ except subprocess.CalledProcessError as e:
793
+ logger.error("FFmpeg command failed with exit code %d.", e.returncode)
794
+ logger.error("FFmpeg stderr:\n%s", e.stderr)
795
+ logger.error("FFmpeg stdout:\n%s", e.stdout)
796
+ # Clean up potentially partially created files in the target directory within the expected range
797
+ logger.warning("Attempting cleanup of potentially incomplete frames in %s", output_dir)
798
+ for i in range(start_frame, end_frame):
799
+ potential_file = output_dir / f"frame_{i:07d}.{ext}"
800
+ if potential_file.exists():
801
+ try:
802
+ potential_file.unlink()
803
+ except OSError as unlink_err:
804
+ logger.error("Failed to delete potential frame %s during cleanup: %s", potential_file, unlink_err)
805
+ raise RuntimeError(f"FFmpeg frame range extraction failed for {video_path}") from e
806
+ except Exception as e:
807
+ logger.error("An unexpected error occurred during FFmpeg execution: %s", e, exc_info=True)
808
+ raise RuntimeError(f"Unexpected error during FFmpeg frame range extraction for {video_path}") from e
809
+
810
+ # Collect paths of extracted frames matching the pattern and expected range
811
+ # FFmpeg might create files outside the exact range depending on version/flags,
812
+ # so filter explicitly.
813
+ extracted_files = []
814
+ for i in range(start_frame, end_frame):
815
+ frame_file = output_dir / f"frame_{i:07d}.{ext}"
816
+ if frame_file.exists():
817
+ extracted_files.append(frame_file)
818
+ else:
819
+ # This might happen if ffmpeg fails silently for some frames or if the video ends early.
820
+ logger.warning("Expected frame file %s not found after extraction.", frame_file)
821
+
822
+
823
+ logger.info("Found %d extracted frame files in range [%d, %d) for video %s.", len(extracted_files), start_frame, end_frame, video_path.name)
824
+ return extracted_files
825
+
826
+ __all__ = [
827
+ "is_ffmpeg_available", # ADDED
828
+ "check_ffmpeg_availability", # ADDED
829
+ "get_stream_info",
830
+ "assemble_video_from_frames", # Updated name
831
+ "transcode_video",
832
+ "transcode_videofile_if_required",
833
+ "extract_frames",
834
+ "extract_frame_range", # Add new function to __all__
835
+ ]