endoreg-db 0.8.4.4__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 (36) hide show
  1. endoreg_db/management/commands/load_ai_model_data.py +2 -1
  2. endoreg_db/management/commands/setup_endoreg_db.py +11 -7
  3. endoreg_db/models/media/pdf/raw_pdf.py +241 -97
  4. endoreg_db/models/media/video/pipe_1.py +30 -33
  5. endoreg_db/models/media/video/video_file.py +300 -187
  6. endoreg_db/models/metadata/model_meta_logic.py +15 -1
  7. endoreg_db/models/metadata/sensitive_meta_logic.py +391 -70
  8. endoreg_db/serializers/__init__.py +26 -55
  9. endoreg_db/serializers/misc/__init__.py +1 -1
  10. endoreg_db/serializers/misc/file_overview.py +65 -35
  11. endoreg_db/serializers/misc/{vop_patient_data.py → sensitive_patient_data.py} +1 -1
  12. endoreg_db/serializers/video_examination.py +198 -0
  13. endoreg_db/services/lookup_service.py +228 -58
  14. endoreg_db/services/lookup_store.py +174 -30
  15. endoreg_db/services/pdf_import.py +585 -282
  16. endoreg_db/services/video_import.py +340 -101
  17. endoreg_db/urls/__init__.py +36 -23
  18. endoreg_db/urls/label_video_segments.py +2 -0
  19. endoreg_db/urls/media.py +3 -2
  20. endoreg_db/views/__init__.py +6 -3
  21. endoreg_db/views/media/pdf_media.py +3 -1
  22. endoreg_db/views/media/video_media.py +1 -1
  23. endoreg_db/views/media/video_segments.py +187 -259
  24. endoreg_db/views/pdf/__init__.py +5 -8
  25. endoreg_db/views/pdf/pdf_stream.py +187 -0
  26. endoreg_db/views/pdf/reimport.py +110 -94
  27. endoreg_db/views/requirement/lookup.py +171 -287
  28. endoreg_db/views/video/__init__.py +0 -2
  29. endoreg_db/views/video/video_examination_viewset.py +202 -289
  30. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.6.1.dist-info}/METADATA +1 -1
  31. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.6.1.dist-info}/RECORD +33 -34
  32. endoreg_db/views/pdf/pdf_media.py +0 -239
  33. endoreg_db/views/pdf/pdf_stream_views.py +0 -127
  34. endoreg_db/views/video/video_media.py +0 -158
  35. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.6.1.dist-info}/WHEEL +0 -0
  36. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.6.1.dist-info}/licenses/LICENSE +0 -0
@@ -258,7 +258,7 @@ endoreg_db/management/commands/import_report.py,sha256=vFst-NeQdL-w62yoH4kDamq-2
258
258
  endoreg_db/management/commands/import_video.py,sha256=AMvgi1eN0F_hjhgnNNYIFkJtHfjalBfh2lfDxw6VTzE,17980
259
259
  endoreg_db/management/commands/import_video_with_classification.py,sha256=ulZH5jvAWu_pJ1kI9B3hbIO1-p_BReY0zbIQDS_d9OI,14726
260
260
  endoreg_db/management/commands/init_default_ai_model.py,sha256=98yBigGZ5gkA-b1LPcvzS5x2jAms3pX58fU-TEAcjKw,4669
261
- endoreg_db/management/commands/load_ai_model_data.py,sha256=O_mzGGcpnVacC4EZmEGbTp4EADNXJRsvaZoNFgh2wFU,2867
261
+ endoreg_db/management/commands/load_ai_model_data.py,sha256=ba2z-0qWtweUC6iYyiusH3xGDkpkYNNlIjei3QK7YAA,2887
262
262
  endoreg_db/management/commands/load_ai_model_label_data.py,sha256=jnm2720TsnRTBKF6guwnjLo7sropW_YoRjgyjo1TUr8,2143
263
263
  endoreg_db/management/commands/load_base_db_data.py,sha256=0Go2cYbqfx6MBSeQaHPAq22yeJxOyX25xpmcBcE9Auw,9374
264
264
  endoreg_db/management/commands/load_center_data.py,sha256=GQpbe7dxgbTgd66oBqrBXax-os3ibnPmCeSEFDVauPU,2570
@@ -291,7 +291,7 @@ endoreg_db/management/commands/load_unit_data.py,sha256=tcux-iL-ByT2ApgmHEkLllZS
291
291
  endoreg_db/management/commands/load_user_groups.py,sha256=D7SK2FvZEHoE4TIXNGCjDw5_12MH9bpGZvoS7eEv0Os,1031
292
292
  endoreg_db/management/commands/register_ai_model.py,sha256=KixTfuQR6TUfRmzB5GOos16BFOz7NL4TzLzBkgtPPgE,2510
293
293
  endoreg_db/management/commands/reset_celery_schedule.py,sha256=U-m_FNRTw6LAwJoT9RUE4qrhmQXm7AyFToPcHYyJpIE,386
294
- endoreg_db/management/commands/setup_endoreg_db.py,sha256=C0ooVVmrzX-_ksoma3s3rZtJUcUsxvO4ddm1VhlYiiU,17640
294
+ endoreg_db/management/commands/setup_endoreg_db.py,sha256=vwjb5jH1oBNXtLpPrthbnP1WcwXPgBMoO4K2ZMH5dEI,17886
295
295
  endoreg_db/management/commands/start_filewatcher.py,sha256=3jESBqRiYPa9f35--zd70qQaYnyT0tzRO_b_HJuyteQ,4093
296
296
  endoreg_db/management/commands/storage_management.py,sha256=NpToX59ndwTFNmnSoeppmiPdMvpjSHH7mAdIe4SvUoI,22396
297
297
  endoreg_db/management/commands/summarize_db_content.py,sha256=pOIz3qbY4Ktmh0zV_DKFx971VD0pPx027gCD7a47EL0,10766
@@ -380,17 +380,17 @@ endoreg_db/models/media/__init__.py,sha256=WuomOKk-3kNGDLKoLO_ltnXbo028JDVlCCScd
380
380
  endoreg_db/models/media/frame/__init__.py,sha256=25WriZHocR82rbHr_IMIgBt5z-mkV2O0LVWUpJRnQ5E,45
381
381
  endoreg_db/models/media/frame/frame.py,sha256=6BvNKTAGsC_Q0S27nrvZ2cySDy3LSu5HCxrNcZwIEsA,4408
382
382
  endoreg_db/models/media/pdf/__init__.py,sha256=1fimtZK5LUuP_yqETN6pMj0hxKXuQqeLb1DP5wPmtT4,343
383
- endoreg_db/models/media/pdf/raw_pdf.py,sha256=sBm5C1Pr4N4n8oQw_nNMLN5cnl3efQ-WKWby2_xBauk,26185
383
+ endoreg_db/models/media/pdf/raw_pdf.py,sha256=aUOGyqFAMcOdfPE_BDGM1_e9vk_YQvZgNcXoSxOR2sg,29963
384
384
  endoreg_db/models/media/pdf/report_file.py,sha256=6Vt_TJSB3qaF26qXbyjRSn-s_zN5y4e-PPrN8ZbYS9s,4296
385
385
  endoreg_db/models/media/pdf/report_reader/__init__.py,sha256=LiMooVstXRay5qcB-uZW0wxbcdUvPrfAs_xBwWiuuWc,166
386
386
  endoreg_db/models/media/pdf/report_reader/report_reader_config.py,sha256=wYVDmPSmNIuCTpaaWqoLVgVZFYW-dqGpnAyVBqRLqXE,3445
387
387
  endoreg_db/models/media/pdf/report_reader/report_reader_flag.py,sha256=j9tjbLRenxpWfeaseALl8rV2Dqem9YaM_duS1iJkARU,536
388
388
  endoreg_db/models/media/video/__init__.py,sha256=ifW4SXXN2q6wAuFwSP7XlYskpX7UX6uy0py5mpCCOCM,211
389
389
  endoreg_db/models/media/video/create_from_file.py,sha256=3n4bbzFteEOFDUuEikP0x-StCKI5R5IhyKC7o3kLZ6Y,15128
390
- endoreg_db/models/media/video/pipe_1.py,sha256=yUzTi0pkw2ISsOoFpLmNky6S_V-TEWMxXmPLfB7gUpA,9899
390
+ endoreg_db/models/media/video/pipe_1.py,sha256=ljO3vO2mqqTXLZsKjzMTC6-sW4JRWMVRfJcK0n5CjKg,9740
391
391
  endoreg_db/models/media/video/pipe_2.py,sha256=DnMxW0uOqSsf7-0n9Rlvn7u89U4Jpkv7n6hFpQfUjkQ,4964
392
392
  endoreg_db/models/media/video/refactor_plan.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
393
- endoreg_db/models/media/video/video_file.py,sha256=txlxR8d1OBgt3UEkWvLcGSyLarh0jXLw-z0SAV5KOok,26789
393
+ endoreg_db/models/media/video/video_file.py,sha256=VFRgP1QRtuxzE9l74tCW7Kz5leHdv5d5i3aqtH3Y1vA,30281
394
394
  endoreg_db/models/media/video/video_file_ai.py,sha256=3ABea52FOF1qlrlxHdYhz_M3Kmqfzqtgq7M0prl-FAo,18819
395
395
  endoreg_db/models/media/video/video_file_anonymize.py,sha256=pet1UfSsbSHJJZxq6gDPifAfBWpGyEpD1jEQuSQi0Gg,16027
396
396
  endoreg_db/models/media/video/video_file_frames.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -465,10 +465,10 @@ endoreg_db/models/medical/risk/risk_type.py,sha256=kEugcaWSTEWH_Vxq4dcF80Iv1L4_K
465
465
  endoreg_db/models/metadata/__init__.py,sha256=8I6oLj3YTmeaPGJpL0AWG5gLwp38QzrEggxSkTisv7c,474
466
466
  endoreg_db/models/metadata/frame_ocr_result.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
467
467
  endoreg_db/models/metadata/model_meta.py,sha256=F_r-PTLeNi4J-4EaGCQkGIguhdl7Bwba7_i56ZAjc-4,7589
468
- endoreg_db/models/metadata/model_meta_logic.py,sha256=OhPImTQaB_a3p-h7iaxNNQkip5aaDDIOMXCV2TEyNmk,12215
468
+ endoreg_db/models/metadata/model_meta_logic.py,sha256=vAbNDaoZygH8xOCulWlXoHoR1T0BSvr9kIloxjzhfjo,12533
469
469
  endoreg_db/models/metadata/pdf_meta.py,sha256=BTmpSgqxmPKi0apcNjyrZAS4AFKCPXVdBd6VBeyyv6E,3174
470
470
  endoreg_db/models/metadata/sensitive_meta.py,sha256=ekLHrW-b5uYcjfkRd0EW5ncx5ef8Bu-K6msDkpWCAbk,13034
471
- endoreg_db/models/metadata/sensitive_meta_logic.py,sha256=XN3x3p0cqLlzPSZl7e35JBUXr_QKYSq48vwF1N60N4U,32134
471
+ endoreg_db/models/metadata/sensitive_meta_logic.py,sha256=FRkBX_EgR2cfXO2_7TzllyceJJgU2lfGPkGsPGiLF2E,43119
472
472
  endoreg_db/models/metadata/video_meta.py,sha256=c6xWdLW3uNqJ5VPJXHCxXA3mbXw-b0uR54-TOS3qL2Q,14966
473
473
  endoreg_db/models/metadata/video_prediction_logic.py,sha256=j5N82mHtiomeeIaf1HA65kT5d0htQfJmbI2bJb8mpxQ,7677
474
474
  endoreg_db/models/metadata/video_prediction_meta.py,sha256=EyfctAAAVcW9L0gf76ZBc9-G8MLMcD-tc2kkjaaLH4w,10592
@@ -517,10 +517,11 @@ endoreg_db/queries/sanity/__init_.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
517
517
  endoreg_db/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
518
518
  endoreg_db/schemas/examination_evaluation.py,sha256=G6uBSQf9ZclwnGvMI-xVPd4IzD6GORUHYYRRCr5Qp6k,844
519
519
  endoreg_db/serializers/Frames_NICE_and_PARIS_classifications.py,sha256=q06UKC0Nt4sOJphwN9lEaY28ZAvtcMAlOwT5Zq_sblI,31384
520
- endoreg_db/serializers/__init__.py,sha256=DFcJPYKdCKHgfB8emktMyAWa9nArjzEqRsy0IXLagS8,3386
520
+ endoreg_db/serializers/__init__.py,sha256=LryzaMvorz_qacOnaT8Uu10L8KHHQRtLVaWU-nlQ43g,3589
521
521
  endoreg_db/serializers/anonymization.py,sha256=R5hlk6KiHpQ2JshKlFq5gY6Ed4TrarA74hlvll9w-4s,2576
522
522
  endoreg_db/serializers/examination_serializer.py,sha256=JPI9diMup9e-nSCH6UwU9KulnSKreg6pOwjKCLHWJfs,281
523
523
  endoreg_db/serializers/sensitive_meta_serializer.py,sha256=zF_0HOrdhi9714DExVD1DTsQqDbRIJevkjLU4vZEgYE,10748
524
+ endoreg_db/serializers/video_examination.py,sha256=jotR5PNTA_xEMpqsqdP3PFaie8TO7SGv2Vp18sj8TlY,6937
524
525
  endoreg_db/serializers/_old/raw_pdf_meta_validation.py,sha256=hZaPTY4AA4wO7uAA0-DKLueHwtqnNsJqFR8Apdv6wTk,7608
525
526
  endoreg_db/serializers/_old/raw_video_meta_validation.py,sha256=Owku2MmHjEZR_FpgKUdaG9In9IRwur53NmhJdser9Bs,7542
526
527
  endoreg_db/serializers/_old/video.py,sha256=O7PIt7uJNP5Xy9R9tbWuptCcsrm1dV0382_ymyVgl5w,2634
@@ -557,12 +558,12 @@ endoreg_db/serializers/meta/sensitive_meta_detail.py,sha256=1i-9Osl9vBH7_KObdm8B
557
558
  endoreg_db/serializers/meta/sensitive_meta_update.py,sha256=qy7cM2MhQUJST9YcEXuyYmL8oX4kkKCIcyiz2nXHo-g,5591
558
559
  endoreg_db/serializers/meta/sensitive_meta_verification.py,sha256=gXH2RfJtVPSgnA3Z58FU6kxTZs6vBI14EEZT7lx2NFE,2289
559
560
  endoreg_db/serializers/meta/video_meta.py,sha256=g96J4va5_oIg7rbbVqcyBsy8imgmbiHjysuEEtas0cA,1284
560
- endoreg_db/serializers/misc/__init__.py,sha256=iO8DlpYe5GUIcd4rpHUUzfMt604V8BcoKc4d3YX_WvQ,483
561
- endoreg_db/serializers/misc/file_overview.py,sha256=DsKFBCgGUIrDo5b4879pGBnw1GL8bilr3Pe7xh2lH9I,7210
561
+ endoreg_db/serializers/misc/__init__.py,sha256=MfG55Bb9sNBajmt-QkTOg1qFNSOEFQ6RWHIA_DVtneE,489
562
+ endoreg_db/serializers/misc/file_overview.py,sha256=ImOTX4a_zjboVyQs2IHBFNqhEfZWDvgcrx1bf2XpL2w,7395
563
+ endoreg_db/serializers/misc/sensitive_patient_data.py,sha256=cewApBYKsk0s9T7CGY_FJmuXAYs3hJV3Ra7oYazKndk,5831
562
564
  endoreg_db/serializers/misc/stats.py,sha256=iL8iZseOLeLaoaCDF3MtTQBH3b0yX5ZFkrZbq5MiMGM,1255
563
565
  endoreg_db/serializers/misc/translatable_field_mix_in.py,sha256=ZH8ZUE1-No0DLCcIGk445c00jQcX7M6Yldxik-FaGfg,1724
564
566
  endoreg_db/serializers/misc/upload_job.py,sha256=e8rXbgs7lkh-NAswLBV2pkEngWdfqHmJcIqS1fM0bJY,2306
565
- endoreg_db/serializers/misc/vop_patient_data.py,sha256=DHsp8Uc4ZqvxQMlWG3gVtLjxoA-U8eLu51g-yQLA5AY,5823
566
567
  endoreg_db/serializers/patient/__init__.py,sha256=ogQGq3jGnFP3L7gK9FbcJodhrzdR6_6xAM89sO9dZjM,185
567
568
  endoreg_db/serializers/patient/patient.py,sha256=8GlQw9a6zxhrieuzw0qf2Xryiuj64-Pe-jgYFdGlRdk,3072
568
569
  endoreg_db/serializers/patient/patient_dropdown.py,sha256=0hijgjMbgrUo78Em6hNqRMv5XdQJ42pTXmOZClSDtuQ,964
@@ -595,30 +596,30 @@ endoreg_db/services/__init__.py,sha256=CMLgpzemgjXvKN5HGFD_UVtRAg9XeMIfXvoSu-aUa
595
596
  endoreg_db/services/anonymization.py,sha256=iM-xW1aJm99G3PmkfZtMJmvoaj99c8OASP5-abBNcy0,8986
596
597
  endoreg_db/services/examination_evaluation.py,sha256=jx9IL2PIoBzjiITzs00c1XucE7Ab2LAUydNbErRmtTM,5943
597
598
  endoreg_db/services/finding_description_service.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
598
- endoreg_db/services/lookup_service.py,sha256=A2t07-qlQhFEeNvOhez0GU0sxi7mnN0MIlhYzxj4W1U,10581
599
- endoreg_db/services/lookup_store.py,sha256=8sB2HmJQrnzq5Vfqt-UdaJLHYMRZCxnui9BCCXscnJE,4856
600
- endoreg_db/services/pdf_import.py,sha256=_ll_Vng1SpI7mEKxNRYuNttVnehuJFpKjQGDB8OX7no,47133
599
+ endoreg_db/services/lookup_service.py,sha256=425eHruJ1JbLxiSiv0bTeVXKLoS0tqK74t70epDKj9o,16176
600
+ endoreg_db/services/lookup_store.py,sha256=k-dxq_0oocXJLzLPENmsvfxxN2whCJyCFPo_2wwIsD8,8962
601
+ endoreg_db/services/pdf_import.py,sha256=a-JwePNsXGZEYyVKHSm77K_Q9xoz5gThAbk4fVfotHI,54693
601
602
  endoreg_db/services/polling_coordinator.py,sha256=alnPB-kdMyxbYaxQN9fki9dKrwmAsY3s68bUHWDSNeI,10662
602
603
  endoreg_db/services/pseudonym_service.py,sha256=CJhbtRa6K6SPbphgCZgEMi8AFQtB18CUoBDttFnxEoM,3126
603
604
  endoreg_db/services/requirements_object.py,sha256=290zf8AEbVtCoHhW4Jr7_ud-RvrqYmb1Nz9UBHtTnc0,6164
604
605
  endoreg_db/services/segment_sync.py,sha256=YgHvIHkbW4mqCu0ACf3zjRSZnNfxWwt4gh5syUVXuE0,6400
605
606
  endoreg_db/services/storage_aware_video_processor.py,sha256=kKFK64vXLeBSVkp1YJonU3gFDTeXZ8C4qb9QZZB99SE,13420
606
- endoreg_db/services/video_import.py,sha256=quQ1z_hqJFcrq5pOeq74pqqmnS2I9ybJIh5D0v8qtOk,46514
607
+ endoreg_db/services/video_import.py,sha256=mtEQlHq-n5x-PE1w8CpeCNnkmKpLhbjWflqtK43nBlw,52237
607
608
  endoreg_db/tasks/upload_tasks.py,sha256=OJq7DhNwcbWdXzHY8jz5c51BCVkPN5gSWOz-6Fx6W5M,7799
608
609
  endoreg_db/tasks/video_ingest.py,sha256=kxFuYkHijINV0VabQKCFVpJRv6eCAw07tviONurDgg8,5265
609
610
  endoreg_db/tasks/video_processing_tasks.py,sha256=rZ7Kr49bAR4Q-vALO2SURebrhcJ5hSFGwjF4aULrOao,14089
610
611
  endoreg_db/templates/timeline.html,sha256=H9VXKOecCzqcWWkpNIZXFI29ztg-oxV5uvxMglgoClk,6167
611
612
  endoreg_db/templates/admin/patient_finding_intervention.html,sha256=F3JUKm3HhWIf_xoZZ-SET5d5ZDlm2jMM8g909w1dnYc,10164
612
613
  endoreg_db/templates/admin/start_examination.html,sha256=3K4wirul9KNyB5mN9cpfCSCAyAD6ro19GwxFOY5sZ3A,267
613
- endoreg_db/urls/__init__.py,sha256=xR1QnSBY0mdUUJ8Wkf71063in59ZEKIFgoaw3Ak76Ow,2973
614
+ endoreg_db/urls/__init__.py,sha256=YpT06IceApAkWfQvNmqMku8F_vozWD4UDl6j3BamS_I,3303
614
615
  endoreg_db/urls/anonymization.py,sha256=zLoOOpu4XSha002nOi0fiJUAT8Dff7u7RQFtdU4A6aw,1853
615
616
  endoreg_db/urls/auth.py,sha256=t95D8n3fiOhkUiINVGygrW0jiQiJc_XNmfk2S8pcDxk,440
616
617
  endoreg_db/urls/classification.py,sha256=2JdHsCSpLs5bZWiTsDpWvyLE_OJkf7LrCCOob7H8M-k,1802
617
618
  endoreg_db/urls/examination.py,sha256=IWNIR-hsWKD3nfL27EzMMazcZnOa_imjgk8RL0mql2U,2344
618
619
  endoreg_db/urls/files.py,sha256=qfa8mPDNF9SW4z66B26SCtX7WHyoAo1muS1DR2QwST8,192
619
620
  endoreg_db/urls/label_video_segment_validate.py,sha256=bGWacxIG4pHnGRbnHL_L91ShKWfKCn5X-YXsHGs6i-I,1044
620
- endoreg_db/urls/label_video_segments.py,sha256=Hjc__HjohZnNE-oudqBc_uOIHqiSbQ_zpN4aHzGYRuE,1142
621
- endoreg_db/urls/media.py,sha256=ufFP3GL3fpxYUSlFJfIfg1L6pmUcC_Blao46G3PsEPQ,10699
621
+ endoreg_db/urls/label_video_segments.py,sha256=Xj7Gf-JSq5iB3ubit5aCaLMJcgQXgDmqMSCb-n-CIl8,1144
622
+ endoreg_db/urls/media.py,sha256=VzPx-4XuLQTO73QRWUa3VSw7XzpPN7IXdMOTX3uvFp8,10832
622
623
  endoreg_db/urls/patient.py,sha256=VLKFbEiNgpfYLWFisbHidyoUnoQuEzj1U0SgZDpdPDM,588
623
624
  endoreg_db/urls/report.py,sha256=9i3sOSofB7_PHGByr3DMghZFtcbjKf_U5PVSlxu9d4I,1917
624
625
  endoreg_db/urls/requirements.py,sha256=5d-SD_7nsTA9YXlBeWdZuh-mXh26BkgH29fFBaTLjU8,455
@@ -682,7 +683,7 @@ endoreg_db/utils/video/names.py,sha256=m268j2Ynt94OYH6dYxeL8gzU5ODtFJD4OmzS7l0nB
682
683
  endoreg_db/utils/video/streaming_processor.py,sha256=C-39DtxhSnL7B2cObFE5k829VLXl_Fl0KQFrFP368JA,13747
683
684
  endoreg_db/utils/video/video_splitter.py,sha256=EZEnhNjaUva_9VxjcjScgRSrxsEuifhBjlwIMLX1qaA,3698
684
685
  endoreg_db/views/Frames_NICE_and_PARIS_classifications_views.py,sha256=Lu1JuUD44B6yUAR9pcYLlQ-3g66VTktmzStuO0uGIj4,8752
685
- endoreg_db/views/__init__.py,sha256=L1xXouAYYqIK7zXDvA6qhcEv7gs5b7wc3g8HeFCVFWU,6553
686
+ endoreg_db/views/__init__.py,sha256=7XsOCeLP6x7WbNrmPXNpxh6EO2u3P8mw1v3p_EQEPQc,6607
686
687
  endoreg_db/views/anonymization/__init__.py,sha256=s1_r9j0jPJsKHy1-isjFAlRF3Cw0o8EXxyUP7Xv1Kqo,698
687
688
  endoreg_db/views/anonymization/media_management.py,sha256=ObiXjPa-KsSGs3IfmABE-TVzQZ2U6yJioFh64T7TtKY,16799
688
689
  endoreg_db/views/anonymization/overview.py,sha256=fA4a4tZ4tbnWf3BUP6heAtqmHvinixUnDoI3dqPjTE0,8320
@@ -716,11 +717,11 @@ endoreg_db/views/label_video_segment/label_video_segment_detail.py,sha256=gs1llC
716
717
  endoreg_db/views/label_video_segment/update_lvs_from_annotation.py,sha256=an3Bf4Jxap_vZrHW2XZV72fKkQhP3S2fsB3ZjXRjED8,1599
717
718
  endoreg_db/views/label_video_segment/validate.py,sha256=1R7Ml-XgzZmzooYbPEnIkjm-u6rlvH-Yu5XGulhZEpI,8951
718
719
  endoreg_db/views/media/__init__.py,sha256=JRRICKlyxrVQqN3ORM8WoJzVo2iRq6Mr72d-1REsM0k,1282
719
- endoreg_db/views/media/pdf_media.py,sha256=VqW4jI_PzQklf07SOM7kRhin_6SfZFhCzELDS-4naYY,14463
720
+ endoreg_db/views/media/pdf_media.py,sha256=0xUszgT7wbMQ5iW4ak4p_Xg78l1CMntsKqtnCLYp-EE,14573
720
721
  endoreg_db/views/media/segments.py,sha256=d995_pLD2W6zdrB3bL9bdHrwS22sZXeG19-SLNU38gU,2613
721
722
  endoreg_db/views/media/sensitive_metadata.py,sha256=CuIHLVGkjnBtWJHHERQvRApLzjkFbuRu-HBJEPqnhhQ,10456
722
- endoreg_db/views/media/video_media.py,sha256=wj848cz0qbWhTj8h4LRau_isYgwbn8TMV6yD3uL_0Ow,9640
723
- endoreg_db/views/media/video_segments.py,sha256=tYocxt3ZhQJ8GabTb34-4SFnyKVC71HdSahOfrBTj58,21795
723
+ endoreg_db/views/media/video_media.py,sha256=rt3DmuxHSH52yWW_r1t5ilq4DJ3w571IpUvyz5ilxz8,9632
724
+ endoreg_db/views/media/video_segments.py,sha256=N1gHYRI4Mdn74Amiv_gcuPessPDtXo65RdLTT70ZOOs,20027
724
725
  endoreg_db/views/meta/__init__.py,sha256=cMwPBx55ad5zRX9DILqFm-a_ATc5SRVSMEGyHTC5cOo,470
725
726
  endoreg_db/views/meta/available_files_list.py,sha256=CBaYVkj2YKB5n_J0gop0vXHkpgTE9Aamqt4tdCjTN2I,6181
726
727
  endoreg_db/views/meta/report_meta.py,sha256=oLFTQ5YCVoCKlcYfbiN89--_9drBhMLeW9Ck_tmq08E,1733
@@ -756,23 +757,22 @@ endoreg_db/views/patient_finding_location/__init__.py,sha256=RiUqJl22PkM2Iv6UcV8
756
757
  endoreg_db/views/patient_finding_location/pfl_create.py,sha256=eZEtj4ST_Ue3YURna_x04MUKAeAo3mv_wfcGGlVO07g,2971
757
758
  endoreg_db/views/patient_finding_morphology/__init__.py,sha256=Gw4REKbb14tlKGGf__XlS2Pi2ygA0iCh85LYIHndUy0,113
758
759
  endoreg_db/views/patient_finding_morphology/pfm_create.py,sha256=-OYTH7iHPuSrYxbUrF7fQ6aAC0ES6UhiQ_Qo7tZi7II,2998
759
- endoreg_db/views/pdf/__init__.py,sha256=N3fR1qX7xpt4LMFU0BmJxnHRGoG-nKezj9ifStWVt7M,188
760
- endoreg_db/views/pdf/pdf_media.py,sha256=Ox981PIOVPqhRwZUZ_nkuLZ3L91owZpOzYZRS06C94k,11055
761
- endoreg_db/views/pdf/pdf_stream_views.py,sha256=oLfV60lNAqn2eZgbkRxuoVftDphTYHiSoZaqfX2pcl4,5495
762
- endoreg_db/views/pdf/reimport.py,sha256=GNUUFJS56qaegbhCdD4t3ytdp-EGPb5mRvq6BSe5QQ4,6902
760
+ endoreg_db/views/pdf/__init__.py,sha256=bmDbvDLRmhOYTuPHMMaJMJU67js9t_K6Df987wRs4wU,135
761
+ endoreg_db/views/pdf/pdf_stream.py,sha256=QkF7ggSNk-GZGKT_eOnflMv6Fls-piMmbx1FXrUPy04,7429
762
+ endoreg_db/views/pdf/reimport.py,sha256=9tMIsxZ5R83rC-GBcJt78QBLqmYaEBypFd6ToEeTsfk,6939
763
763
  endoreg_db/views/report/__init__.py,sha256=mw1WncSWijE73mSzkZPWsQ66ii9icZMLmEEEx9iV4BA,241
764
764
  endoreg_db/views/report/report_list.py,sha256=Ziav43FKYux2dsETaX7zUYs-g7__4laHwXEMZSq1Ahs,3940
765
765
  endoreg_db/views/report/report_with_secure_url.py,sha256=vMGgngPGvNkPYxhNjSJG6rZKN0yuSoJNl1vZPiAoh6I,1042
766
766
  endoreg_db/views/report/start_examination.py,sha256=iYC_40S4aJEGsel1Y4boZFAR5UQTPKp9TIWW3oJwgTc,224
767
767
  endoreg_db/views/requirement/__init__.py,sha256=dJzmbtO5-fLyzY3hfV3XHi6BbwElv_cF6R0TpsIaRJw,159
768
768
  endoreg_db/views/requirement/evaluate.py,sha256=_P5b6DoJBOnsGtIzL3usR2tTG1GXBRgsvG0LzQ9f9_A,10666
769
- endoreg_db/views/requirement/lookup.py,sha256=rxZ4zH122QLj32q8pzCe1dsp8YRpwcamqgmvGI3NdeM,8816
769
+ endoreg_db/views/requirement/lookup.py,sha256=BNR7Ai6QdPvdfSYZWMRTzwO8dLvVDuxoqD4YHi3LbaM,12544
770
770
  endoreg_db/views/requirement/lookup_store.py,sha256=LyohEAfHtPy40zDuJUDyN4S02BB_AOAbPM_CZIcHHRQ,4922
771
771
  endoreg_db/views/requirement_lookup/lookup.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
772
772
  endoreg_db/views/requirement_lookup/lookup_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
773
773
  endoreg_db/views/stats/__init__.py,sha256=VHxrW0-RAWYki_89jWWJ0TrD7Nb0D3m-VjYV6bvP2k4,259
774
774
  endoreg_db/views/stats/stats_views.py,sha256=v9ue1BKXdcApOgyolgNiT3hazx1xSVVvo26r91IozWY,8656
775
- endoreg_db/views/video/__init__.py,sha256=wRYF-M3amwlmTRlJ7nAYQZiwS0QgLLPxH3pPtHSQy-A,1288
775
+ endoreg_db/views/video/__init__.py,sha256=KXne9Hcg_rhWogmnmf_ZpbrpnNfJlhwbF0cqBXhYCEY,1196
776
776
  endoreg_db/views/video/correction.py,sha256=VEzpnDPCAhsYatXVRZhGZA5FpFS_t9mENs89zFJsoYo,19750
777
777
  endoreg_db/views/video/reimport.py,sha256=gSDBQ_Bam2xpJIj1SIMoLKnoBDBRgQ9V_TZdK9OICrI,8959
778
778
  endoreg_db/views/video/segmentation.py,sha256=jzsLB95rYnHALSX2E_QNpAM9BpE1pbteOBxcAr_EjZo,11758
@@ -782,13 +782,12 @@ endoreg_db/views/video/video_analyze.py,sha256=vxQHnXRFH5Y6IaWEmEnE4Svkz6Vjw1O9N
782
782
  endoreg_db/views/video/video_apply_mask.py,sha256=spGK4oXMAKhVotJuwZGndbKKws6klveKe35mbX7Rv6k,1630
783
783
  endoreg_db/views/video/video_correction.py,sha256=9mFICVvRSxp0WhGv7ib4d4M1hJs1iIhYmTl2OUIXQAg,767
784
784
  endoreg_db/views/video/video_download_processed.py,sha256=tp5llYJKyQD0WSr4Fvqi-YrgBw2EPPe23E8F8SZbR4w,2000
785
- endoreg_db/views/video/video_examination_viewset.py,sha256=77ujdUh8fzTC5Oe2fo4ESLBkwJ10rGhJLlQBpP1eMbQ,13663
786
- endoreg_db/views/video/video_media.py,sha256=fvMQmSM1a-3rxpx_wK0wee9sRXV3EZz2bF8jLRAeaIQ,7022
785
+ endoreg_db/views/video/video_examination_viewset.py,sha256=ej6CUK1JGFN7JmZOj25g1heaIIAwh6cf1HaYFmcR9EE,8331
787
786
  endoreg_db/views/video/video_meta.py,sha256=C1wBMTtQb_yzEUrhFGAy2UHEWMk_CbU75WXX_5z00sE,1021
788
787
  endoreg_db/views/video/video_processing_history.py,sha256=mhFuS8RG5GV8E-lTtuD0qrq-bIpnUFp8vy9aERfC-J8,770
789
788
  endoreg_db/views/video/video_remove_frames.py,sha256=2FmvNrSPM0fUXiBxINN6vBUUDCqDlBkNcGR3WsLDgKo,1696
790
789
  endoreg_db/views/video/video_stream.py,sha256=kLyuf0ORTmsLeYUQkTQ6iRYqlIQozWhMMR3Lhfe_trk,12148
791
- endoreg_db-0.8.4.4.dist-info/METADATA,sha256=GGQH5jWgXwF4K0rTdGb5jxxeVfRbbMdtmLf9b7MO_M0,14719
792
- endoreg_db-0.8.4.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
793
- endoreg_db-0.8.4.4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
794
- endoreg_db-0.8.4.4.dist-info/RECORD,,
790
+ endoreg_db-0.8.6.1.dist-info/METADATA,sha256=x0wow7qLlIoTyTfPlAhaEQCeJAlL2H_0uigFCE5A4rU,14719
791
+ endoreg_db-0.8.6.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
792
+ endoreg_db-0.8.6.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
793
+ endoreg_db-0.8.6.1.dist-info/RECORD,,
@@ -1,239 +0,0 @@
1
- from django.http import FileResponse, Http404, StreamingHttpResponse
2
- import mimetypes
3
- import os
4
- import logging
5
- import re
6
- from ...models import RawPdfFile
7
- from ...serializers._old.raw_pdf_meta_validation import PDFFileForMetaSerializer, SensitiveMetaUpdateSerializer
8
- from rest_framework.views import APIView
9
- from rest_framework.response import Response
10
- from rest_framework import status
11
- from ...models import SensitiveMeta
12
- from django.views.decorators.clickjacking import xframe_options_sameorigin
13
- from django.utils.decorators import method_decorator
14
- from django.db import transaction
15
- from django.urls import reverse
16
- from django.utils.encoding import iri_to_uri
17
- from endoreg_db.utils.paths import PDF_DIR, STORAGE_DIR
18
- from .pdf_stream_views import ClosingFileWrapper
19
-
20
- logger = logging.getLogger(__name__)
21
- _RANGE_RE = re.compile(r"bytes=(\d+)-(\d*)")
22
-
23
- class PDFMediaView(APIView):
24
- """
25
- Unified API for PDFs to support frontend flows:
26
- - Without `id`: returns next PDF metadata (including anonymized_text) and stream URLs
27
- - With `id`: streams the PDF (original by default; `?variant=anonymized` for anonymized)
28
- - Integrates with Media Management expectations (clean deletion after validation is handled elsewhere)
29
- """
30
-
31
- def get(self, request):
32
- """
33
- Handles both:
34
- - Fetching PDF metadata (if `id` is NOT provided)
35
- - Streaming the actual PDF file (if `id` is provided)
36
- """
37
- pdf_id = request.GET.get("id")
38
- last_id = request.GET.get("last_id")
39
-
40
- if pdf_id:
41
- return self.serve_pdf_file(pdf_id)
42
- else:
43
- return self.fetch_pdf_metadata(last_id)
44
-
45
- def fetch_pdf_metadata(self, last_id):
46
- """
47
- Fetches the first or next available PDF metadata and provides stream URLs.
48
- """
49
- pdf_entry = PDFFileForMetaSerializer.get_next_pdf(last_id)
50
- if pdf_entry is None:
51
- return Response({"error": "No more PDFs available."}, status=status.HTTP_404_NOT_FOUND)
52
-
53
- serialized_pdf = PDFFileForMetaSerializer(pdf_entry, context={'request': self.request})
54
-
55
- # Build stream URLs pointing to this unified endpoint
56
- try:
57
- media_url = reverse('pdf_media')
58
- except Exception:
59
- media_url = "/api/pdf/media/"
60
- stream_url = f"{media_url}?id={pdf_entry.id}"
61
- anon_stream_url = f"{media_url}?id={pdf_entry.id}&variant=anonymized"
62
-
63
- data = dict(serialized_pdf.data)
64
- data.update({
65
- 'stream_url': iri_to_uri(self.request.build_absolute_uri(stream_url)),
66
- 'anonymized_stream_url': iri_to_uri(self.request.build_absolute_uri(anon_stream_url)),
67
- 'pdf_id': pdf_entry.id,
68
- 'has_anonymized': bool(getattr(pdf_entry, 'anonymized_file', None) and getattr(pdf_entry.anonymized_file, 'name', None)),
69
- })
70
- return Response(data, status=status.HTTP_200_OK)
71
-
72
- @method_decorator(xframe_options_sameorigin)
73
- def serve_pdf_file(self, pdf_id):
74
- """
75
- Streams the actual PDF file (original or anonymized) with Range support.
76
- Query param `variant=anonymized` selects anonymized file; default is original.
77
- """
78
- variant = (self.request.GET.get('variant') or 'original').lower()
79
- range_header = self.request.headers.get("Range")
80
-
81
- try:
82
- pdf_entry = RawPdfFile.objects.get(id=pdf_id)
83
- except RawPdfFile.DoesNotExist:
84
- return Response({"error": "Invalid PDF ID."}, status=status.HTTP_400_BAD_REQUEST)
85
-
86
- # Choose file according to variant
87
- file_field = None
88
- if variant == 'anonymized' and getattr(pdf_entry, 'anonymized_file', None):
89
- file_field = pdf_entry.anonymized_file
90
- else:
91
- file_field = pdf_entry.file
92
-
93
- if not file_field:
94
- return Response({"error": "PDF file not found."}, status=status.HTTP_404_NOT_FOUND)
95
-
96
- # Resolve path and attempt self-heal for originals
97
- try:
98
- file_path = file_field.path
99
- except Exception:
100
- file_path = None
101
-
102
- if not file_path or not os.path.exists(file_path):
103
- if variant != 'anonymized':
104
- # Try to self-heal original reference to sensitive storage
105
- sensitive_path = os.path.join(str(PDF_DIR), "sensitive", f"{pdf_entry.pdf_hash}.pdf")
106
- if os.path.exists(sensitive_path):
107
- try:
108
- relative_name = os.path.relpath(sensitive_path, str(STORAGE_DIR))
109
- if getattr(pdf_entry.file, 'name', None) != relative_name:
110
- pdf_entry.file.name = relative_name
111
- pdf_entry.save(update_fields=['file'])
112
- logger.info("Self-healed PDF file reference for ID %s -> %s", pdf_entry.id, pdf_entry.file.path)
113
- file_path = sensitive_path
114
- except Exception as e:
115
- logger.error("Failed to self-heal file path for PDF %s: %s", pdf_entry.id, e)
116
- file_path = sensitive_path
117
- # If still missing (or anonymized missing), fail
118
- if not file_path or not os.path.exists(file_path):
119
- raise Http404("PDF file not found on server.")
120
-
121
- # Prepare headers
122
- safe_filename = os.path.basename(getattr(file_field, 'name', None) or f"document_{pdf_id}.pdf")
123
- if not safe_filename.endswith('.pdf'):
124
- safe_filename += '.pdf'
125
-
126
- file_size = os.path.getsize(file_path)
127
-
128
- # Range support
129
- if range_header:
130
- match = _RANGE_RE.match(range_header)
131
- if match:
132
- start = int(match.group(1))
133
- end = int(match.group(2) or file_size - 1)
134
-
135
- if start < 0 or start >= file_size:
136
- raise Http404("Invalid range")
137
- if end >= file_size:
138
- end = file_size - 1
139
-
140
- chunk_size = end - start + 1
141
- try:
142
- fh = open(file_path, 'rb')
143
- fh.seek(start)
144
- resp = StreamingHttpResponse(
145
- ClosingFileWrapper(fh, blksize=8192),
146
- status=206,
147
- content_type="application/pdf",
148
- )
149
- resp["Content-Length"] = str(chunk_size)
150
- resp["Content-Range"] = f"bytes {start}-{end}/{file_size}"
151
- resp["Accept-Ranges"] = "bytes"
152
- resp["Content-Disposition"] = f'inline; filename="{safe_filename}"'
153
- return resp
154
- except (OSError, IOError) as e:
155
- logger.error(f"Error opening PDF file for range request: {e}")
156
- raise Http404("Error accessing PDF file")
157
-
158
- # Fallback: serve full file
159
- mime_type, _ = mimetypes.guess_type(file_path)
160
- try:
161
- fh = open(file_path, 'rb')
162
- response = FileResponse(fh, content_type=mime_type or "application/pdf")
163
- response["Content-Length"] = str(file_size)
164
- response["Accept-Ranges"] = "bytes"
165
- response["Content-Disposition"] = f'inline; filename="{safe_filename}"'
166
- return response
167
- except (OSError, IOError) as e:
168
- logger.error(f"Error opening PDF file: {e}")
169
- raise Http404("Error accessing PDF file")
170
-
171
- class UpdateSensitiveMetaView(APIView):
172
- """
173
- API endpoint to update patient details in the SensitiveMeta table.
174
- Handles partial updates (only edited fields) and raw file deletion after validation acceptance.
175
- """
176
-
177
- @transaction.atomic
178
- def patch(self, request, *args, **kwargs):
179
- """
180
- Updates the provided fields for a specific patient record.
181
- Only updates fields that are sent in the request.
182
- Automatically deletes raw PDF files when validation is accepted.
183
- """
184
- sensitive_meta_id = request.data.get("sensitive_meta_id")
185
-
186
- if not sensitive_meta_id:
187
- return Response({"error": "sensitive_meta_id is required."}, status=status.HTTP_400_BAD_REQUEST)
188
-
189
- try:
190
- sensitive_meta = SensitiveMeta.objects.get(id=sensitive_meta_id)
191
- except SensitiveMeta.DoesNotExist:
192
- return Response({"error": "Patient record not found."}, status=status.HTTP_404_NOT_FOUND)
193
-
194
- is_accepting_validation = request.data.get("is_verified", False)
195
- delete_raw_files = request.data.get("delete_raw_files", False)
196
- if is_accepting_validation:
197
- delete_raw_files = True
198
- logger.info(f"Validation accepted for PDF SensitiveMeta {sensitive_meta_id}, marking raw files for deletion")
199
-
200
- serializer = SensitiveMetaUpdateSerializer(sensitive_meta, data=request.data, partial=True)
201
-
202
- if serializer.is_valid():
203
- updated_sm = serializer.save()
204
- if delete_raw_files and updated_sm.is_verified:
205
- try:
206
- pdf_file = RawPdfFile.objects.filter(sensitive_meta=updated_sm).first()
207
- if pdf_file:
208
- self._schedule_raw_file_deletion(pdf_file)
209
- logger.info(f"Scheduled raw file deletion for PDF {pdf_file.id}")
210
- else:
211
- logger.warning(f"No PDF file found for SensitiveMeta {sensitive_meta_id}")
212
- except Exception as e:
213
- logger.error(f"Error scheduling raw file deletion for PDF SensitiveMeta {sensitive_meta_id}: {e}")
214
- return Response({"message": "Patient information updated successfully.", "updated_data": serializer.data}, status=status.HTTP_200_OK)
215
-
216
- return Response({"error": "Invalid data.", "details": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
217
-
218
- def _schedule_raw_file_deletion(self, pdf_file):
219
- """
220
- Schedule deletion of raw PDF file after validation acceptance.
221
- Deletes the original (sensitive) file but keeps anonymized_file for frontend.
222
- """
223
- try:
224
- def cleanup_raw_files():
225
- try:
226
- if pdf_file.file and getattr(pdf_file.file, 'path', None) and os.path.exists(pdf_file.file.path):
227
- logger.info(f"Deleting original (sensitive) PDF file: {pdf_file.file.path}")
228
- os.remove(pdf_file.file.path)
229
- pdf_file.file = None
230
- pdf_file.save(update_fields=['file'])
231
- logger.info(f"Successfully deleted original file for PDF {pdf_file.id}")
232
- else:
233
- logger.info(f"Original file already deleted or not found for PDF {pdf_file.id}")
234
- except Exception as e:
235
- logger.error(f"Error during raw file cleanup for PDF {pdf_file.id}: {e}")
236
- transaction.on_commit(cleanup_raw_files)
237
- except Exception as e:
238
- logger.error(f"Error scheduling raw file deletion for PDF {pdf_file.id}: {e}")
239
- raise
@@ -1,127 +0,0 @@
1
- import re
2
- import logging
3
- from django.http import FileResponse, StreamingHttpResponse, Http404
4
- from rest_framework.views import APIView
5
- from ...utils.permissions import EnvironmentAwarePermission
6
- from endoreg_db.models import RawPdfFile
7
- import os
8
- from django.views.decorators.clickjacking import xframe_options_sameorigin
9
-
10
- logger = logging.getLogger(__name__)
11
- _RANGE_RE = re.compile(r"bytes=(\d+)-(\d*)")
12
-
13
- class ClosingFileWrapper:
14
- """Custom file wrapper that ensures file is closed after streaming"""
15
- def __init__(self, file_handle, blksize=8192):
16
- self.file_handle = file_handle
17
- self.blksize = blksize
18
-
19
- def __iter__(self):
20
- return self
21
-
22
- def __next__(self):
23
- data = self.file_handle.read(self.blksize)
24
- if not data:
25
- self.file_handle.close()
26
- raise StopIteration
27
- return data
28
-
29
- def close(self):
30
- if hasattr(self.file_handle, 'close'):
31
- self.file_handle.close()
32
-
33
- class PDFStreamView(APIView):
34
- """
35
- Streams a PDF file with correct HTTP range support and proper file handle management.
36
- """
37
- permission_classes = [EnvironmentAwarePermission]
38
-
39
- @xframe_options_sameorigin
40
- def get(self, request, pdf_id: int, *args, **kwargs):
41
- try:
42
- pdf_obj = RawPdfFile.objects.filter(pk=pdf_id).first()
43
- if not pdf_obj or not pdf_obj.file:
44
- logger.warning(f"PDF not found: ID {pdf_id}")
45
- raise Http404("PDF not found")
46
-
47
- # Check if file exists on filesystem
48
- try:
49
- file_path = pdf_obj.file.path
50
- if not os.path.exists(file_path):
51
- logger.error(f"PDF file does not exist on filesystem: {file_path}")
52
- raise Http404("PDF file not found on filesystem")
53
-
54
- file_size = os.path.getsize(file_path)
55
- except (OSError, IOError, AttributeError) as e:
56
- logger.error(f"Error accessing PDF file {pdf_id}: {e}")
57
- raise Http404("PDF file not accessible")
58
-
59
- # Generate safe filename
60
- safe_filename = os.path.basename(pdf_obj.file.name) if pdf_obj.file.name else f"document_{pdf_id}.pdf"
61
- if not safe_filename.endswith('.pdf'):
62
- safe_filename += '.pdf'
63
-
64
- # Handle Range requests
65
- range_header = request.headers.get("Range")
66
- if range_header:
67
- logger.debug(f"Range request for PDF {pdf_id}: {range_header}")
68
- match = _RANGE_RE.match(range_header)
69
- if match:
70
- start = int(match.group(1))
71
- end = int(match.group(2) or file_size - 1)
72
-
73
- # Validate range
74
- if start >= file_size or start < 0:
75
- logger.warning(f"Invalid range start {start} for file size {file_size}")
76
- raise Http404("Invalid range")
77
-
78
- if end >= file_size:
79
- end = file_size - 1
80
-
81
- chunk_size = end - start + 1
82
-
83
- try:
84
- file_handle = open(file_path, "rb")
85
- file_handle.seek(start)
86
-
87
- logger.debug(f"Serving PDF {pdf_id} range {start}-{end}/{file_size}")
88
-
89
- response = StreamingHttpResponse(
90
- ClosingFileWrapper(file_handle, blksize=8192),
91
- status=206,
92
- content_type="application/pdf",
93
- )
94
- response["Content-Length"] = str(chunk_size)
95
- response["Content-Range"] = f"bytes {start}-{end}/{file_size}"
96
- response["Accept-Ranges"] = "bytes"
97
- response["Content-Disposition"] = f'inline; filename="{safe_filename}"'
98
-
99
- return response
100
- except (OSError, IOError) as e:
101
- logger.error(f"Error opening PDF file for range request: {e}")
102
- raise Http404("Error accessing PDF file")
103
- else:
104
- logger.warning(f"Invalid Range header format: {range_header}")
105
-
106
- # Serve entire file using FileResponse (automatically handles file closing)
107
- logger.debug(f"Serving full PDF {pdf_id} ({file_size} bytes)")
108
-
109
- try:
110
- file_handle = open(file_path, "rb")
111
- response = FileResponse(
112
- file_handle,
113
- content_type="application/pdf"
114
- )
115
- response["Content-Length"] = str(file_size)
116
- response["Accept-Ranges"] = "bytes"
117
- response["Content-Disposition"] = f'inline; filename="{safe_filename}"'
118
-
119
- # FileResponse will take ownership of file_handle and close it after response
120
- return response
121
- except (OSError, IOError) as e:
122
- logger.error(f"Error opening PDF file: {e}")
123
- raise Http404("Error accessing PDF file")
124
-
125
- except Exception as e:
126
- logger.error(f"Unexpected error streaming PDF {pdf_id}: {e}", exc_info=True)
127
- raise Http404("Error streaming PDF")