OpenREM 1.0.0b2__py3-none-any.whl → 1.0.0b3__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.
- openrem/locale/de/LC_MESSAGES/django.po +1060 -1059
- openrem/locale/django.pot +973 -972
- openrem/locale/es_MX/LC_MESSAGES/django.po +1049 -1048
- openrem/locale/it/LC_MESSAGES/django.po +1044 -1043
- openrem/locale/lt/LC_MESSAGES/django.po +989 -988
- openrem/locale/nb_NO/LC_MESSAGES/django.po +985 -984
- openrem/locale/pt_BR/LC_MESSAGES/django.po +1003 -1002
- openrem/manage.py +10 -10
- openrem/openremproject/__init__.py +1 -1
- openrem/openremproject/local_settings.py.linux +128 -128
- openrem/openremproject/local_settings.py.windows +144 -144
- openrem/openremproject/local_settings.py.windows-sqlite3 +129 -129
- openrem/openremproject/settings.py +278 -278
- openrem/openremproject/urls.py +32 -32
- openrem/openremproject/wsgi.py.example +28 -28
- openrem/remapp/__init__.py +2 -2
- openrem/remapp/admin.py +31 -31
- openrem/remapp/exports/ct_export.py +780 -753
- openrem/remapp/exports/dx_export.py +817 -805
- openrem/remapp/exports/export_common.py +931 -951
- openrem/remapp/exports/export_common_pandas.py +2422 -0
- openrem/remapp/exports/exportviews.py +815 -860
- openrem/remapp/exports/mg_csv_nhsbsp.py +292 -292
- openrem/remapp/exports/mg_export.py +673 -510
- openrem/remapp/exports/nm_export.py +796 -575
- openrem/remapp/exports/rf_export.py +1418 -1431
- openrem/remapp/extractors/ct_philips.py +424 -414
- openrem/remapp/extractors/ct_toshiba.py +2116 -2108
- openrem/remapp/extractors/dx.py +1033 -952
- openrem/remapp/extractors/extract_common.py +817 -817
- openrem/remapp/extractors/import_views.py +426 -426
- openrem/remapp/extractors/mam.py +685 -672
- openrem/remapp/extractors/nm_image.py +439 -431
- openrem/remapp/extractors/ptsizecsv2db.py +368 -368
- openrem/remapp/extractors/rdsr.py +667 -654
- openrem/remapp/extractors/rdsr_methods.py +1771 -1768
- openrem/remapp/extractors/rrdsr_methods.py +630 -622
- openrem/remapp/fixtures/openskin_safelist.json +11 -11
- openrem/remapp/forms.py +2286 -2277
- openrem/remapp/interface/chart_functions.py +2412 -2393
- openrem/remapp/interface/mod_filters.py +1241 -1243
- openrem/remapp/migrations/0001_initial.py.1-0-upgrade +1043 -1043
- openrem/remapp/models.py +3418 -3407
- openrem/remapp/netdicom/dicomviews.py +681 -683
- openrem/remapp/netdicom/qrscu.py +2646 -2646
- openrem/remapp/netdicom/tools.py +134 -134
- openrem/remapp/static/css/bootstrap-theme.css +587 -587
- openrem/remapp/static/css/bootstrap-theme.min.css +4 -4
- openrem/remapp/static/css/bootstrap.css +6800 -6800
- openrem/remapp/static/css/bootstrap.min.css +4 -4
- openrem/remapp/static/css/datepicker3.css +790 -790
- openrem/remapp/static/css/jquery.qtip.min.css +2 -2
- openrem/remapp/static/css/openrem-extra.css +442 -442
- openrem/remapp/static/css/openrem.css +96 -96
- openrem/remapp/static/css/registration.css +34 -34
- openrem/remapp/static/fonts/glyphicons-halflings-regular.svg +287 -287
- openrem/remapp/static/js/bootstrap-datepicker.js +1671 -1671
- openrem/remapp/static/js/bootstrap.js +2363 -2363
- openrem/remapp/static/js/bootstrap.min.js +6 -6
- openrem/remapp/static/js/charts/chartCommonFunctions.js +75 -75
- openrem/remapp/static/js/charts/chartFullScreen.js +41 -41
- openrem/remapp/static/js/charts/ctChartAjax.js +331 -331
- openrem/remapp/static/js/charts/dxChartAjax.js +290 -290
- openrem/remapp/static/js/charts/mgChartAjax.js +144 -144
- openrem/remapp/static/js/charts/nmChartAjax.js +64 -64
- openrem/remapp/static/js/charts/plotly-2.35.2.min.js +8 -0
- openrem/remapp/static/js/charts/rfChartAjax.js +128 -128
- openrem/remapp/static/js/chroma.min.js +32 -32
- openrem/remapp/static/js/datepicker.js +5 -5
- openrem/remapp/static/js/dicom.js +115 -115
- openrem/remapp/static/js/django_reverse/reverse.js +13 -13
- openrem/remapp/static/js/formatDate.js +7 -7
- openrem/remapp/static/js/html5shiv.min.js +8 -8
- openrem/remapp/static/js/jquery-1.11.0.min.js +4 -4
- openrem/remapp/static/js/npm.js +12 -12
- openrem/remapp/static/js/respond.min.js +4 -4
- openrem/remapp/static/js/skin-dose-maps/jquery.qtip.min.js +4 -4
- openrem/remapp/static/js/skin-dose-maps/rfSkinDoseMap3dHUDObject.js +112 -112
- openrem/remapp/static/js/skin-dose-maps/rfSkinDoseMap3dObject.js +367 -367
- openrem/remapp/static/js/skin-dose-maps/rfSkinDoseMap3dPersonObject.js +158 -158
- openrem/remapp/static/js/skin-dose-maps/rfSkinDoseMapColourScaleObject.js +153 -153
- openrem/remapp/static/js/skin-dose-maps/rfSkinDoseMapObject.js +367 -367
- openrem/remapp/static/js/skin-dose-maps/rfSkinDoseMapping.js +584 -584
- openrem/remapp/static/js/skin-dose-maps/rfSkinDoseMapping3d.js +255 -255
- openrem/remapp/static/js/skin-dose-maps/rfSkinDoseMappingAjax.js +267 -212
- openrem/remapp/static/js/skin-dose-maps/three.min.js +835 -835
- openrem/remapp/static/js/sorttable.js +495 -495
- openrem/remapp/templates/base.html +253 -253
- openrem/remapp/templates/registration/changepassword.html +25 -25
- openrem/remapp/templates/registration/changepassworddone.html +12 -12
- openrem/remapp/templates/registration/login.html +42 -42
- openrem/remapp/templates/remapp/backgroundtaskmaximumrows_form.html +29 -29
- openrem/remapp/templates/remapp/base.html +1 -1
- openrem/remapp/templates/remapp/ctdetail.html +235 -235
- openrem/remapp/templates/remapp/ctfiltered.html +310 -310
- openrem/remapp/templates/remapp/dicomdeletesettings_form.html +31 -31
- openrem/remapp/templates/remapp/dicomqr.html +147 -147
- openrem/remapp/templates/remapp/dicomquerydetails.html +83 -83
- openrem/remapp/templates/remapp/dicomqueryimages.html +49 -49
- openrem/remapp/templates/remapp/dicomqueryseries.html +109 -109
- openrem/remapp/templates/remapp/dicomquerysummary.html +48 -48
- openrem/remapp/templates/remapp/dicomremoteqr_confirm_delete.html +60 -60
- openrem/remapp/templates/remapp/dicomremoteqr_form.html +32 -32
- openrem/remapp/templates/remapp/dicomstorescp_confirm_delete.html +53 -53
- openrem/remapp/templates/remapp/dicomstorescp_form.html +48 -48
- openrem/remapp/templates/remapp/dicomsummary.html +257 -257
- openrem/remapp/templates/remapp/displaychartoptions.html +184 -184
- openrem/remapp/templates/remapp/displayhomepageoptions.html +57 -57
- openrem/remapp/templates/remapp/displayname-count.html +6 -6
- openrem/remapp/templates/remapp/displayname-last-date.html +3 -3
- openrem/remapp/templates/remapp/displayname-modality.html +86 -105
- openrem/remapp/templates/remapp/displayname-skinmap.html +18 -18
- openrem/remapp/templates/remapp/displaynameupdate.html +100 -100
- openrem/remapp/templates/remapp/displaynameview.html +222 -219
- openrem/remapp/templates/remapp/dxdetail.html +176 -176
- openrem/remapp/templates/remapp/dxfiltered.html +324 -324
- openrem/remapp/templates/remapp/exports-active.html +25 -25
- openrem/remapp/templates/remapp/exports-complete.html +35 -35
- openrem/remapp/templates/remapp/exports-error.html +26 -26
- openrem/remapp/templates/remapp/exports-queue.html +18 -18
- openrem/remapp/templates/remapp/exports.html +191 -191
- openrem/remapp/templates/remapp/failed_summary_list.html +27 -27
- openrem/remapp/templates/remapp/filteredbase.html +162 -162
- openrem/remapp/templates/remapp/highdosemetricalertsettings_form.html +76 -76
- openrem/remapp/templates/remapp/home-list-modalities.html +94 -94
- openrem/remapp/templates/remapp/home.html +202 -202
- openrem/remapp/templates/remapp/list_filters.html +24 -24
- openrem/remapp/templates/remapp/mgdetail.html +160 -138
- openrem/remapp/templates/remapp/mgfiltered.html +311 -311
- openrem/remapp/templates/remapp/nmdetail.html +300 -300
- openrem/remapp/templates/remapp/nmfiltered.html +255 -255
- openrem/remapp/templates/remapp/notpatient.html +190 -190
- openrem/remapp/templates/remapp/notpatientindicators_form_base.html +81 -81
- openrem/remapp/templates/remapp/notpatientindicatorsid_confirm_delete.html +54 -54
- openrem/remapp/templates/remapp/notpatientindicatorsid_form.html +23 -23
- openrem/remapp/templates/remapp/notpatientindicatorsname_confirm_delete.html +54 -54
- openrem/remapp/templates/remapp/notpatientindicatorsname_form.html +23 -23
- openrem/remapp/templates/remapp/notpatientindicatorsname_form_base.html +85 -85
- openrem/remapp/templates/remapp/openskinsafelist_add.html +130 -130
- openrem/remapp/templates/remapp/openskinsafelist_confirm_delete.html +100 -100
- openrem/remapp/templates/remapp/openskinsafelist_form.html +207 -207
- openrem/remapp/templates/remapp/patientidsettings_form.html +83 -83
- openrem/remapp/templates/remapp/populate_summary_progress.html +83 -83
- openrem/remapp/templates/remapp/populate_summary_progress_error.html +36 -36
- openrem/remapp/templates/remapp/review_failed_imports.html +157 -157
- openrem/remapp/templates/remapp/review_failed_study.html +41 -41
- openrem/remapp/templates/remapp/review_studies_delete_button.html +20 -20
- openrem/remapp/templates/remapp/review_study.html +19 -19
- openrem/remapp/templates/remapp/review_summary_list.html +245 -245
- openrem/remapp/templates/remapp/rf_dose_alert_email_template.html +14 -1
- openrem/remapp/templates/remapp/rfalertnotificationsview.html +59 -59
- openrem/remapp/templates/remapp/rfdetail.html +547 -543
- openrem/remapp/templates/remapp/rfdetailbase.html +18 -18
- openrem/remapp/templates/remapp/rffiltered.html +404 -404
- openrem/remapp/templates/remapp/sizeimports.html +119 -119
- openrem/remapp/templates/remapp/sizeprocess.html +96 -96
- openrem/remapp/templates/remapp/sizeupload.html +110 -110
- openrem/remapp/templates/remapp/skindosemapcalcsettings_form.html +28 -28
- openrem/remapp/templates/remapp/standardname-modality.html +69 -69
- openrem/remapp/templates/remapp/standardnames_confirm_delete.html +71 -71
- openrem/remapp/templates/remapp/standardnames_form.html +87 -87
- openrem/remapp/templates/remapp/standardnamesettings_form.html +41 -41
- openrem/remapp/templates/remapp/standardnamesrefreshall.html +92 -92
- openrem/remapp/templates/remapp/standardnameview.html +103 -103
- openrem/remapp/templates/remapp/study_confirm_delete.html +147 -147
- openrem/remapp/templates/remapp/task_admin.html +265 -265
- openrem/remapp/templates/remapp/tasks.html +76 -76
- openrem/remapp/templatetags/formfilters.py +13 -13
- openrem/remapp/templatetags/proper_paginate.py +38 -38
- openrem/remapp/templatetags/remappduration.py +36 -36
- openrem/remapp/templatetags/sigdig.py +38 -38
- openrem/remapp/templatetags/sort_class_property_value.py +15 -15
- openrem/remapp/templatetags/update_variable.py +20 -20
- openrem/remapp/templatetags/url_replace.py +25 -25
- openrem/remapp/tests/test_charts_common.py +202 -202
- openrem/remapp/tests/test_charts_ct.py +7111 -7111
- openrem/remapp/tests/test_charts_dx.py +3513 -3513
- openrem/remapp/tests/test_charts_mg.py +1116 -1115
- openrem/remapp/tests/test_dcmdatetime.py +189 -189
- openrem/remapp/tests/test_dicom_qr.py +2580 -2580
- openrem/remapp/tests/test_display_name.py +274 -274
- openrem/remapp/tests/test_export_ct_xlsx.py +272 -248
- openrem/remapp/tests/test_export_dx_xlsx.py +137 -134
- openrem/remapp/tests/test_export_mammo_csv.py +242 -242
- openrem/remapp/tests/test_export_rf_xlsx.py +246 -246
- openrem/remapp/tests/test_files/DX-Im-DRGEM.dcm +0 -0
- openrem/remapp/tests/test_files/MG-RDSR-GEPristina-2D.dcm +0 -0
- openrem/remapp/tests/test_files/MG-RDSR-GEPristina-DBT.dcm +0 -0
- openrem/remapp/tests/test_files/MG-RDSR-Giotto-DBT.dcm +0 -0
- openrem/remapp/tests/test_files/skin_map_alphenix.py +590 -590
- openrem/remapp/tests/test_files/skin_map_zee.py +354 -354
- openrem/remapp/tests/test_filters_ct.py +321 -321
- openrem/remapp/tests/test_filters_dx.py +92 -92
- openrem/remapp/tests/test_filters_mammo.py +183 -183
- openrem/remapp/tests/test_filters_rf.py +118 -118
- openrem/remapp/tests/test_get_values.py +72 -72
- openrem/remapp/tests/test_hash_id.py +65 -65
- openrem/remapp/tests/test_import_ct_esr_ge.py +3034 -3034
- openrem/remapp/tests/test_import_ct_philips_rdsr.py +42 -42
- openrem/remapp/tests/test_import_ct_rdsr_multiple.py +256 -256
- openrem/remapp/tests/test_import_ct_rdsr_siemens.py +827 -827
- openrem/remapp/tests/test_import_ct_rdsr_spectrumdynamics.py +91 -91
- openrem/remapp/tests/test_import_ct_rdsr_toshiba_dosecheck.py +67 -67
- openrem/remapp/tests/test_import_ct_rdsr_toshiba_multivaluesd.py +33 -33
- openrem/remapp/tests/test_import_ct_rdsr_toshiba_pixelmed.py +118 -118
- openrem/remapp/tests/test_import_ct_sc_philips.py +44 -44
- openrem/remapp/tests/test_import_dual_rdsr.py +110 -110
- openrem/remapp/tests/test_import_dx.py +1267 -1191
- openrem/remapp/tests/test_import_dx_rdsr.py +1250 -1253
- openrem/remapp/tests/test_import_mam.py +438 -438
- openrem/remapp/tests/test_import_mg_im_hol_proj.py +46 -46
- openrem/remapp/tests/test_import_mg_rdsr.py +586 -586
- openrem/remapp/tests/test_import_nm_image.py +420 -420
- openrem/remapp/tests/test_import_nm_siemens_rdsr.py +396 -396
- openrem/remapp/tests/test_import_px.py +161 -161
- openrem/remapp/tests/test_import_rf_rdsr.py +420 -418
- openrem/remapp/tests/test_missing_date.py +42 -42
- openrem/remapp/tests/test_not_patient.py +60 -60
- openrem/remapp/tests/test_openskin.py +272 -272
- openrem/remapp/tests/test_patient_id_settings.py +72 -72
- openrem/remapp/tests/test_pt_size_import.py +232 -232
- openrem/remapp/tests/test_rf_detail.py +113 -113
- openrem/remapp/tests/test_rf_high_dose_alert.py +361 -361
- openrem/remapp/tools/background.py +361 -361
- openrem/remapp/tools/check_standard_name_status.py +47 -0
- openrem/remapp/tools/check_uid.py +70 -70
- openrem/remapp/tools/dcmdatetime.py +248 -248
- openrem/remapp/tools/default_import.py +44 -47
- openrem/remapp/tools/get_values.py +230 -230
- openrem/remapp/tools/hash_id.py +58 -58
- openrem/remapp/tools/make_skin_map.py +448 -406
- openrem/remapp/tools/not_patient_indicators.py +72 -72
- openrem/remapp/tools/openskin/calc_exp_map.py +173 -173
- openrem/remapp/tools/openskin/geomclass.py +475 -475
- openrem/remapp/tools/openskin/geomfunc.py +433 -432
- openrem/remapp/tools/openskin/skinmap.py +417 -417
- openrem/remapp/tools/populate_summary.py +185 -193
- openrem/remapp/tools/save_skin_map_structure.py +73 -73
- openrem/remapp/tools/send_high_dose_alert_emails.py +238 -207
- openrem/remapp/urls.py +456 -448
- openrem/remapp/version.py +11 -11
- openrem/remapp/views.py +1147 -1052
- openrem/remapp/views_admin.py +3876 -3936
- openrem/remapp/views_charts_ct.py +2110 -2058
- openrem/remapp/views_charts_dx.py +1906 -1836
- openrem/remapp/views_charts_mg.py +1349 -1196
- openrem/remapp/views_charts_nm.py +535 -535
- openrem/remapp/views_charts_rf.py +1219 -1241
- openrem/remapp/views_openskin.py +379 -384
- openrem/sample-config/openrem-consumer.service +12 -12
- openrem/sample-config/openrem-gunicorn.service +13 -13
- openrem/sample-config/openrem-server +14 -13
- openrem/sample-config/openrem_orthanc_config_linux.lua +454 -454
- openrem/sample-config/openrem_orthanc_config_windows.lua +455 -455
- openrem/sample-config/queue-init.bat +73 -73
- openrem/scripts/openrem_ctphilips.py +25 -25
- openrem/scripts/openrem_cttoshiba.py +28 -28
- openrem/scripts/openrem_dx.py +22 -22
- openrem/scripts/openrem_mg.py +22 -22
- openrem/scripts/openrem_nm.py +22 -22
- openrem/scripts/openrem_ptsizecsv.py +17 -17
- openrem/scripts/openrem_qr.py +12 -12
- openrem/scripts/openrem_rdsr.py +25 -25
- {OpenREM-1.0.0b2.dist-info → openrem-1.0.0b3.dist-info}/METADATA +39 -29
- openrem-1.0.0b3.dist-info/RECORD +379 -0
- {OpenREM-1.0.0b2.dist-info → openrem-1.0.0b3.dist-info}/WHEEL +1 -1
- {OpenREM-1.0.0b2.dist-info → openrem-1.0.0b3.dist-info/licenses}/COPYING-GPLv3 +674 -674
- {OpenREM-1.0.0b2.dist-info → openrem-1.0.0b3.dist-info/licenses}/LICENSE +22 -22
- OpenREM-1.0.0b2.dist-info/RECORD +0 -373
- openrem/remapp/static/js/charts/plotly-2.17.1.min.js +0 -8
- {OpenREM-1.0.0b2.data → openrem-1.0.0b3.data}/scripts/openrem_ctphilips.py +0 -0
- {OpenREM-1.0.0b2.data → openrem-1.0.0b3.data}/scripts/openrem_cttoshiba.py +0 -0
- {OpenREM-1.0.0b2.data → openrem-1.0.0b3.data}/scripts/openrem_dx.py +0 -0
- {OpenREM-1.0.0b2.data → openrem-1.0.0b3.data}/scripts/openrem_mg.py +0 -0
- {OpenREM-1.0.0b2.data → openrem-1.0.0b3.data}/scripts/openrem_nm.py +0 -0
- {OpenREM-1.0.0b2.data → openrem-1.0.0b3.data}/scripts/openrem_ptsizecsv.py +0 -0
- {OpenREM-1.0.0b2.data → openrem-1.0.0b3.data}/scripts/openrem_qr.py +0 -0
- {OpenREM-1.0.0b2.data → openrem-1.0.0b3.data}/scripts/openrem_rdsr.py +0 -0
- {OpenREM-1.0.0b2.dist-info → openrem-1.0.0b3.dist-info}/top_level.txt +0 -0
|
@@ -1,654 +1,667 @@
|
|
|
1
|
-
# OpenREM - Radiation Exposure Monitoring tools for the physicist
|
|
2
|
-
# Copyright (C) 2012,2013 The Royal Marsden NHS Foundation Trust
|
|
3
|
-
#
|
|
4
|
-
# This program is free software: you can redistribute it and/or modify
|
|
5
|
-
# it under the terms of the GNU General Public License as published by
|
|
6
|
-
# the Free Software Foundation, either version 3 of the License, or
|
|
7
|
-
# (at your option) any later version.
|
|
8
|
-
#
|
|
9
|
-
# This program is distributed in the hope that it will be useful,
|
|
10
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
-
# GNU General Public License for more details.
|
|
13
|
-
#
|
|
14
|
-
# Additional permission under section 7 of GPLv3:
|
|
15
|
-
# You shall not make any use of the name of The Royal Marsden NHS
|
|
16
|
-
# Foundation trust in connection with this Program in any press or
|
|
17
|
-
# other public announcement without the prior written consent of
|
|
18
|
-
# The Royal Marsden NHS Foundation Trust.
|
|
19
|
-
#
|
|
20
|
-
# You should have received a copy of the GNU General Public License
|
|
21
|
-
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
22
|
-
|
|
23
|
-
"""
|
|
24
|
-
.. module:: rdsr.
|
|
25
|
-
:synopsis: Module to extract radiation dose related data from DICOM Radiation SR objects
|
|
26
|
-
or Radiopharmaceutical Radiation SR objects
|
|
27
|
-
|
|
28
|
-
.. moduleauthor:: Ed McDonagh
|
|
29
|
-
|
|
30
|
-
"""
|
|
31
|
-
from collections import OrderedDict
|
|
32
|
-
from datetime import timedelta
|
|
33
|
-
import logging
|
|
34
|
-
import os
|
|
35
|
-
import sys
|
|
36
|
-
|
|
37
|
-
import django
|
|
38
|
-
from django.db.models import Sum, ObjectDoesNotExist
|
|
39
|
-
import pydicom
|
|
40
|
-
|
|
41
|
-
from openrem.remapp.tools.background import (
|
|
42
|
-
record_task_error_exit,
|
|
43
|
-
record_task_related_query,
|
|
44
|
-
record_task_info,
|
|
45
|
-
run_in_background_with_limits,
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
# setup django/OpenREM.
|
|
49
|
-
basepath = os.path.dirname(__file__)
|
|
50
|
-
projectpath = os.path.abspath(os.path.join(basepath, "..", ".."))
|
|
51
|
-
if projectpath not in sys.path:
|
|
52
|
-
sys.path.insert(1, projectpath)
|
|
53
|
-
os.environ["DJANGO_SETTINGS_MODULE"] = "openremproject.settings"
|
|
54
|
-
django.setup()
|
|
55
|
-
|
|
56
|
-
from ..tools.check_uid import record_sop_instance_uid
|
|
57
|
-
from ..tools.get_values import (
|
|
58
|
-
get_value_kw,
|
|
59
|
-
)
|
|
60
|
-
from ..tools.make_skin_map import
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
g.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
"""
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
If
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
If dataset
|
|
251
|
-
|
|
252
|
-
If dataset has
|
|
253
|
-
If dataset
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
:
|
|
260
|
-
drop_old_continue
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
)
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
)
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
"
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
"
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
g.
|
|
417
|
-
if
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
"
|
|
437
|
-
)[0]
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
)
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
)
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
)
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
)
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
"
|
|
618
|
-
)
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
)
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
1
|
+
# OpenREM - Radiation Exposure Monitoring tools for the physicist
|
|
2
|
+
# Copyright (C) 2012,2013 The Royal Marsden NHS Foundation Trust
|
|
3
|
+
#
|
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# Additional permission under section 7 of GPLv3:
|
|
15
|
+
# You shall not make any use of the name of The Royal Marsden NHS
|
|
16
|
+
# Foundation trust in connection with this Program in any press or
|
|
17
|
+
# other public announcement without the prior written consent of
|
|
18
|
+
# The Royal Marsden NHS Foundation Trust.
|
|
19
|
+
#
|
|
20
|
+
# You should have received a copy of the GNU General Public License
|
|
21
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
.. module:: rdsr.
|
|
25
|
+
:synopsis: Module to extract radiation dose related data from DICOM Radiation SR objects
|
|
26
|
+
or Radiopharmaceutical Radiation SR objects
|
|
27
|
+
|
|
28
|
+
.. moduleauthor:: Ed McDonagh
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
from collections import OrderedDict
|
|
32
|
+
from datetime import timedelta
|
|
33
|
+
import logging
|
|
34
|
+
import os
|
|
35
|
+
import sys
|
|
36
|
+
|
|
37
|
+
import django
|
|
38
|
+
from django.db.models import Sum, ObjectDoesNotExist
|
|
39
|
+
import pydicom
|
|
40
|
+
|
|
41
|
+
from openrem.remapp.tools.background import (
|
|
42
|
+
record_task_error_exit,
|
|
43
|
+
record_task_related_query,
|
|
44
|
+
record_task_info,
|
|
45
|
+
run_in_background_with_limits,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# setup django/OpenREM.
|
|
49
|
+
basepath = os.path.dirname(__file__)
|
|
50
|
+
projectpath = os.path.abspath(os.path.join(basepath, "..", ".."))
|
|
51
|
+
if projectpath not in sys.path:
|
|
52
|
+
sys.path.insert(1, projectpath)
|
|
53
|
+
os.environ["DJANGO_SETTINGS_MODULE"] = "openremproject.settings"
|
|
54
|
+
django.setup()
|
|
55
|
+
|
|
56
|
+
from ..tools.check_uid import record_sop_instance_uid
|
|
57
|
+
from ..tools.get_values import (
|
|
58
|
+
get_value_kw,
|
|
59
|
+
)
|
|
60
|
+
from ..tools.make_skin_map import (
|
|
61
|
+
make_skin_map,
|
|
62
|
+
skin_dose_maps_enabled_for_xray_system,
|
|
63
|
+
)
|
|
64
|
+
from ..tools.send_high_dose_alert_emails import send_rf_high_dose_alert_email
|
|
65
|
+
from .extract_common import ( # pylint: disable=wrong-import-order, wrong-import-position
|
|
66
|
+
ct_event_type_count,
|
|
67
|
+
patient_module_attributes,
|
|
68
|
+
populate_mammo_agd_summary,
|
|
69
|
+
populate_dx_rf_summary,
|
|
70
|
+
populate_rf_delta_weeks_summary,
|
|
71
|
+
add_standard_names,
|
|
72
|
+
generalstudymoduleattributes,
|
|
73
|
+
generalequipmentmoduleattributes,
|
|
74
|
+
patientstudymoduleattributes,
|
|
75
|
+
)
|
|
76
|
+
from .rdsr_methods import projectionxrayradiationdose
|
|
77
|
+
from .rrdsr_methods import _radiopharmaceuticalradiationdose
|
|
78
|
+
from remapp.models import ( # pylint: disable=wrong-import-order, wrong-import-position
|
|
79
|
+
AccumIntegratedProjRadiogDose,
|
|
80
|
+
DicomDeleteSettings,
|
|
81
|
+
GeneralStudyModuleAttr,
|
|
82
|
+
HighDoseMetricAlertSettings,
|
|
83
|
+
PKsForSummedRFDoseStudiesInDeltaWeeks,
|
|
84
|
+
SkinDoseMapCalcSettings,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
logger = logging.getLogger(
|
|
88
|
+
"remapp.extractors.rdsr"
|
|
89
|
+
) # Explicitly named so that it is still handled when using __main__
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _rdsr_rrdsr_contents(dataset, g):
|
|
93
|
+
try:
|
|
94
|
+
template_identifier = dataset.ContentTemplateSequence[0].TemplateIdentifier
|
|
95
|
+
except AttributeError:
|
|
96
|
+
try:
|
|
97
|
+
if dataset.ContentSequence[0].ConceptCodeSequence[0].CodeValue == "113704":
|
|
98
|
+
template_identifier = "10001"
|
|
99
|
+
elif (
|
|
100
|
+
dataset.ConceptNameCodeSequence[0].CodingSchemeDesignator
|
|
101
|
+
== "99SMS_RADSUM"
|
|
102
|
+
and dataset.ConceptNameCodeSequence[0].CodeValue == "C-10"
|
|
103
|
+
):
|
|
104
|
+
template_identifier = "10001"
|
|
105
|
+
elif dataset.ConceptCodeSequence[0].CodeValue == "113500":
|
|
106
|
+
template_identifier = "10021"
|
|
107
|
+
else:
|
|
108
|
+
logger.error(
|
|
109
|
+
"Study UID {0} of modality {1} has no template sequence - incomplete RDSR. "
|
|
110
|
+
"Aborting.".format(
|
|
111
|
+
g.study_instance_uid,
|
|
112
|
+
get_value_kw("ManufacturerModelName", dataset),
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
g.delete()
|
|
116
|
+
return
|
|
117
|
+
except AttributeError:
|
|
118
|
+
logger.error(
|
|
119
|
+
"Study UID {0} of modality {1} has no template sequence - incomplete RDSR. Aborting.".format(
|
|
120
|
+
g.study_instance_uid, get_value_kw("ManufacturerModelName", dataset)
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
g.delete()
|
|
124
|
+
return
|
|
125
|
+
if template_identifier == "10001":
|
|
126
|
+
projectionxrayradiationdose(dataset, g, "projection")
|
|
127
|
+
elif template_identifier == "10011":
|
|
128
|
+
projectionxrayradiationdose(dataset, g, "ct")
|
|
129
|
+
elif template_identifier == "10021":
|
|
130
|
+
_radiopharmaceuticalradiationdose(dataset, g)
|
|
131
|
+
g.save()
|
|
132
|
+
if not g.requested_procedure_code_meaning:
|
|
133
|
+
if "RequestAttributesSequence" in dataset and dataset[0x40, 0x275].VM:
|
|
134
|
+
# Ugly hack to prevent issues with zero length LS16 sequence
|
|
135
|
+
req = dataset.RequestAttributesSequence
|
|
136
|
+
g.requested_procedure_code_meaning = get_value_kw(
|
|
137
|
+
"RequestedProcedureDescription", req[0]
|
|
138
|
+
)
|
|
139
|
+
# Sometimes the above is true, but there is no RequestedProcedureDescription in that sequence, but
|
|
140
|
+
# there is a basic field as below.
|
|
141
|
+
if not g.requested_procedure_code_meaning:
|
|
142
|
+
g.requested_procedure_code_meaning = get_value_kw(
|
|
143
|
+
"RequestedProcedureDescription", dataset
|
|
144
|
+
)
|
|
145
|
+
g.save()
|
|
146
|
+
else:
|
|
147
|
+
g.requested_procedure_code_meaning = get_value_kw(
|
|
148
|
+
"RequestedProcedureDescription", dataset
|
|
149
|
+
)
|
|
150
|
+
g.save()
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
number_of_events_ct = (
|
|
154
|
+
g.ctradiationdose_set.get().ctirradiationeventdata_set.count()
|
|
155
|
+
)
|
|
156
|
+
except ObjectDoesNotExist:
|
|
157
|
+
number_of_events_ct = 0
|
|
158
|
+
try:
|
|
159
|
+
number_of_events_proj = (
|
|
160
|
+
g.projectionxrayradiationdose_set.get().irradeventxraydata_set.count()
|
|
161
|
+
)
|
|
162
|
+
except ObjectDoesNotExist:
|
|
163
|
+
number_of_events_proj = 0
|
|
164
|
+
g.number_of_events = number_of_events_ct + number_of_events_proj
|
|
165
|
+
g.save()
|
|
166
|
+
if template_identifier == "10011":
|
|
167
|
+
ct_event_type_count(g)
|
|
168
|
+
try:
|
|
169
|
+
g.total_dlp = (
|
|
170
|
+
g.ctradiationdose_set.get()
|
|
171
|
+
.ctaccumulateddosedata_set.get()
|
|
172
|
+
.ct_dose_length_product_total
|
|
173
|
+
)
|
|
174
|
+
g.save()
|
|
175
|
+
except ObjectDoesNotExist:
|
|
176
|
+
logger.warning(
|
|
177
|
+
"Study UID {0} of modality {1}. Unable to set summary total_dlp".format(
|
|
178
|
+
g.study_instance_uid, get_value_kw("ManufacturerModelName", dataset)
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
elif template_identifier == "10001":
|
|
182
|
+
if g.modality_type == "MG":
|
|
183
|
+
populate_mammo_agd_summary(g)
|
|
184
|
+
else:
|
|
185
|
+
populate_dx_rf_summary(g)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _get_existing_event_uids(study):
|
|
189
|
+
"""
|
|
190
|
+
Returns all event uids stored for this study
|
|
191
|
+
"""
|
|
192
|
+
existing_event_uids = set()
|
|
193
|
+
s = study.ctradiationdose_set.first()
|
|
194
|
+
if s is not None:
|
|
195
|
+
for event in s.ctirradiationeventdata_set.all():
|
|
196
|
+
existing_event_uids.add(event.irradiation_event_uid)
|
|
197
|
+
|
|
198
|
+
s = study.projectionxrayradiationdose_set.first()
|
|
199
|
+
if s is not None:
|
|
200
|
+
for event in s.irradeventxraydata_set.all():
|
|
201
|
+
existing_event_uids.add(event.irradiation_event_uid)
|
|
202
|
+
|
|
203
|
+
s = study.radiopharmaceuticalradiationdose_set.first()
|
|
204
|
+
if s is not None:
|
|
205
|
+
for event in s.radiopharmaceuticaladministrationeventdata_set.all():
|
|
206
|
+
existing_event_uids.add(event.radiopharmaceutical_administration_event_uid)
|
|
207
|
+
|
|
208
|
+
return existing_event_uids
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _update_recorded_objects(g, dataset, existing_sop_instance_uids=None):
|
|
212
|
+
new_sop_instance_uid = dataset.SOPInstanceUID
|
|
213
|
+
record_sop_instance_uid(g, new_sop_instance_uid)
|
|
214
|
+
if existing_sop_instance_uids is not None:
|
|
215
|
+
for sop_instance_uid in existing_sop_instance_uids:
|
|
216
|
+
record_sop_instance_uid(g, sop_instance_uid)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _get_dataset_event_uids(dataset):
|
|
220
|
+
"""
|
|
221
|
+
Collect the uids from all events that can be in a dataset
|
|
222
|
+
and that we care about
|
|
223
|
+
"""
|
|
224
|
+
new_event_uids = set()
|
|
225
|
+
for content in dataset.ContentSequence:
|
|
226
|
+
if content.ValueType and content.ValueType == "CONTAINER":
|
|
227
|
+
if content.ConceptNameCodeSequence[0].CodeMeaning in (
|
|
228
|
+
"CT Acquisition",
|
|
229
|
+
"Irradiation Event X-Ray Data",
|
|
230
|
+
"Radiopharmaceutical Administration",
|
|
231
|
+
):
|
|
232
|
+
for item in content.ContentSequence:
|
|
233
|
+
if item.ConceptNameCodeSequence[0].CodeMeaning in (
|
|
234
|
+
"Irradiation Event UID",
|
|
235
|
+
"Radiopharmaceutical Administration Event UID",
|
|
236
|
+
):
|
|
237
|
+
new_event_uids.add("{0}".format(item.UID))
|
|
238
|
+
return new_event_uids
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _handle_study_already_existing(
|
|
242
|
+
dataset,
|
|
243
|
+
):
|
|
244
|
+
"""
|
|
245
|
+
This function checks wheter there is already a study with the same instance uid as dataset.
|
|
246
|
+
|
|
247
|
+
If yes it checks the data in the dataset and the existing study
|
|
248
|
+
returning different strategies for continuing the import:
|
|
249
|
+
|
|
250
|
+
If dataset was imported already: Abort the import
|
|
251
|
+
If dataset contains subset of events of existing study: Abort the import
|
|
252
|
+
If dataset has same events as existing study: Abort the import
|
|
253
|
+
If dataset has more events than existing study (Events of existing are subset):
|
|
254
|
+
Delete old study and reimport
|
|
255
|
+
If dataset has different events than existing study: Create a second study and import
|
|
256
|
+
If dataset is rrdsr and only images have been imported to the study: Complete study with data from the rrdsr
|
|
257
|
+
|
|
258
|
+
:return: A dict with possible keys {status, existing_sop_instance_uids, study}, where
|
|
259
|
+
:status: One of 'abort', 'continue' (Create the study and maybe save old instance uids to it),
|
|
260
|
+
'drop_old_continue' (Delete old and reimport),
|
|
261
|
+
'retry' (Wait then call this function again, there are partially imported studies), append_rrdsr'
|
|
262
|
+
:existing_sop_instance_uids: The sop instance uids of all files imported for this study. For e.g.
|
|
263
|
+
drop_old_continue status this should be saved onto the new study entry. (See _update_recorded_objects)
|
|
264
|
+
:study: The study that was used to make the decision what to do next.
|
|
265
|
+
Only added for status='append_rrdsr' or status='drop_old_continue' at the moment
|
|
266
|
+
"""
|
|
267
|
+
existing_sop_instance_uids = set()
|
|
268
|
+
|
|
269
|
+
study_uid = dataset.StudyInstanceUID
|
|
270
|
+
existing_study_uid_match = GeneralStudyModuleAttr.objects.filter(
|
|
271
|
+
study_instance_uid__exact=study_uid
|
|
272
|
+
)
|
|
273
|
+
if existing_study_uid_match:
|
|
274
|
+
new_sop_instance_uid = dataset.SOPInstanceUID
|
|
275
|
+
for existing_study in existing_study_uid_match.order_by("pk"):
|
|
276
|
+
for processed_object in existing_study.objectuidsprocessed_set.all():
|
|
277
|
+
existing_sop_instance_uids.add(processed_object.sop_instance_uid)
|
|
278
|
+
if new_sop_instance_uid in existing_sop_instance_uids:
|
|
279
|
+
# We've dealt with this object before...
|
|
280
|
+
logger.debug(
|
|
281
|
+
"Import match on Study Instance UID {0} and object SOP Instance UID {1}. "
|
|
282
|
+
"Will not import.".format(study_uid, new_sop_instance_uid)
|
|
283
|
+
)
|
|
284
|
+
record_task_error_exit("Study already in db")
|
|
285
|
+
return {
|
|
286
|
+
"status": "abort",
|
|
287
|
+
"existing_sop_instance_uids": existing_sop_instance_uids,
|
|
288
|
+
}
|
|
289
|
+
# Either we've not seen it before, or it wasn't recorded when we did.
|
|
290
|
+
# Next find the event UIDs in the RDSR being imported
|
|
291
|
+
new_event_uids = _get_dataset_event_uids(dataset)
|
|
292
|
+
logger.debug(
|
|
293
|
+
"Import match on StudyInstUID {0}. New RDSR event UIDs {1}".format(
|
|
294
|
+
study_uid, new_event_uids
|
|
295
|
+
)
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
# Now check which event UIDs are in the database already
|
|
299
|
+
existing_event_uids = OrderedDict()
|
|
300
|
+
for i, existing_study in enumerate(existing_study_uid_match.order_by("pk")):
|
|
301
|
+
existing_event_uids[i] = set()
|
|
302
|
+
existing_event_uids[i] = _get_existing_event_uids(existing_study)
|
|
303
|
+
logger.debug(
|
|
304
|
+
"Import match on StudyInstUID {0}. Existing event UIDs {1}".format(
|
|
305
|
+
study_uid, existing_event_uids
|
|
306
|
+
)
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
# Now compare the two
|
|
310
|
+
for study_index, uid_list in list(existing_event_uids.items()):
|
|
311
|
+
if uid_list == new_event_uids:
|
|
312
|
+
# New RDSR is the same as the existing one
|
|
313
|
+
logger.debug(
|
|
314
|
+
"Import match on StudyInstUID {0}. Event level match, will not import.".format(
|
|
315
|
+
study_uid
|
|
316
|
+
)
|
|
317
|
+
)
|
|
318
|
+
record_sop_instance_uid(
|
|
319
|
+
existing_study_uid_match[study_index], new_sop_instance_uid
|
|
320
|
+
)
|
|
321
|
+
record_task_error_exit("Study already in db")
|
|
322
|
+
return {
|
|
323
|
+
"status": "abort",
|
|
324
|
+
"existing_sop_instance_uids": existing_sop_instance_uids,
|
|
325
|
+
}
|
|
326
|
+
elif new_event_uids.issubset(uid_list):
|
|
327
|
+
# New RDSR has the same but fewer events than existing one
|
|
328
|
+
logger.debug(
|
|
329
|
+
"Import match on StudyInstUID {0}. New RDSR events are subset of existing events. "
|
|
330
|
+
"Will not import.".format(study_uid)
|
|
331
|
+
)
|
|
332
|
+
record_sop_instance_uid(
|
|
333
|
+
existing_study_uid_match[study_index], new_sop_instance_uid
|
|
334
|
+
)
|
|
335
|
+
record_task_error_exit("Study already in db")
|
|
336
|
+
return {
|
|
337
|
+
"status": "abort",
|
|
338
|
+
"existing_sop_instance_uids": existing_sop_instance_uids,
|
|
339
|
+
}
|
|
340
|
+
elif uid_list.issubset(new_event_uids):
|
|
341
|
+
# New RDSR has the existing events and more
|
|
342
|
+
# Check existing one had finished importing
|
|
343
|
+
logger.debug(
|
|
344
|
+
"Import match on StudyInstUID {0}. Existing events are subset of new events. Will"
|
|
345
|
+
" import.".format(study_uid)
|
|
346
|
+
)
|
|
347
|
+
return {
|
|
348
|
+
"status": "drop_old_continue",
|
|
349
|
+
"existing_sop_instance_uids": existing_sop_instance_uids,
|
|
350
|
+
"study": existing_study_uid_match[study_index],
|
|
351
|
+
}
|
|
352
|
+
elif None in uid_list:
|
|
353
|
+
# This happens for NM studies where only images have been imported so far
|
|
354
|
+
# because they add a RadiopharmaceuticalAdministrationEventData Object without UID.
|
|
355
|
+
logger.debug(
|
|
356
|
+
f"Import match on StudyInstUID {study_uid}. There is already a NM study for"
|
|
357
|
+
"which only Images where imported so far. Will delete the "
|
|
358
|
+
"RadiopharmaceuticalAdministrationEventData and RadiopharmaceuticalRadioationDose"
|
|
359
|
+
"and reimport, but keep the PET_Series data if present."
|
|
360
|
+
)
|
|
361
|
+
study = existing_study_uid_match[study_index]
|
|
362
|
+
if study.modality_type == "NM" and (
|
|
363
|
+
dataset.SOPClassUID
|
|
364
|
+
== "1.2.840.10008.5.1.4.1.1.88.68" # Radiopharmaceutical Radiation Dose SR
|
|
365
|
+
and dataset.ConceptNameCodeSequence[0].CodeValue
|
|
366
|
+
== "113500" # Radiopharmaceutical Radiation Dose Report
|
|
367
|
+
):
|
|
368
|
+
tmp = study.radiopharmaceuticalradiationdose_set.first()
|
|
369
|
+
if tmp is not None:
|
|
370
|
+
tmp = tmp.radiopharmaceuticaladministrationeventdata_set.first()
|
|
371
|
+
if tmp is not None:
|
|
372
|
+
if tmp.radiopharmaceutical_administration_event_uid is None:
|
|
373
|
+
return {
|
|
374
|
+
"status": "append_rrdsr",
|
|
375
|
+
"existing_sop_instance_uids": existing_sop_instance_uids,
|
|
376
|
+
"study": study,
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return {
|
|
380
|
+
"status": "continue",
|
|
381
|
+
"existing_sop_instance_uids": existing_sop_instance_uids,
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def _rdsr2db(dataset):
|
|
386
|
+
if "StudyInstanceUID" in dataset:
|
|
387
|
+
study_uid = dataset.StudyInstanceUID
|
|
388
|
+
study_uid = dataset.StudyInstanceUID
|
|
389
|
+
record_task_info(f"Study UID: {study_uid.replace('.', '. ')}")
|
|
390
|
+
record_task_related_query(study_uid)
|
|
391
|
+
existing = _handle_study_already_existing(dataset)
|
|
392
|
+
|
|
393
|
+
if existing["status"] == "abort":
|
|
394
|
+
return
|
|
395
|
+
elif existing["status"] == "append_rrdsr":
|
|
396
|
+
_update_recorded_objects(existing["study"], dataset)
|
|
397
|
+
_rdsr_rrdsr_contents(dataset, existing["study"])
|
|
398
|
+
radios = existing["study"].radiopharmaceuticalradiationdose_set.all()
|
|
399
|
+
if (
|
|
400
|
+
radios[0]
|
|
401
|
+
.radiopharmaceuticaladministrationeventdata_set.get()
|
|
402
|
+
.radiopharmaceutical_administration_event_uid
|
|
403
|
+
is None
|
|
404
|
+
):
|
|
405
|
+
radio_old, radio_new = (radios[0], radios[1])
|
|
406
|
+
else:
|
|
407
|
+
radio_new, radio_old = (radios[0], radios[1])
|
|
408
|
+
radio_old.petseries_set.update(radiopharmaceutical_radiation_dose=radio_new)
|
|
409
|
+
radio_old.delete()
|
|
410
|
+
return
|
|
411
|
+
elif existing["status"] == "drop_old_continue":
|
|
412
|
+
existing["study"].delete()
|
|
413
|
+
elif existing["status"] == "continue":
|
|
414
|
+
pass # We are allowed to proceed importing
|
|
415
|
+
|
|
416
|
+
g = GeneralStudyModuleAttr.objects.create()
|
|
417
|
+
if not g: # Allows import to be aborted if no template found
|
|
418
|
+
return
|
|
419
|
+
g.save()
|
|
420
|
+
if existing["status"] == "drop_old_continue":
|
|
421
|
+
_update_recorded_objects(g, dataset, existing["existing_sop_instance_uids"])
|
|
422
|
+
else:
|
|
423
|
+
_update_recorded_objects(g, dataset)
|
|
424
|
+
generalstudymoduleattributes(dataset, g, logger)
|
|
425
|
+
generalequipmentmoduleattributes(dataset, g)
|
|
426
|
+
_rdsr_rrdsr_contents(dataset, g)
|
|
427
|
+
patientstudymoduleattributes(dataset, g)
|
|
428
|
+
patient_module_attributes(dataset, g)
|
|
429
|
+
|
|
430
|
+
try:
|
|
431
|
+
SkinDoseMapCalcSettings.objects.get()
|
|
432
|
+
except ObjectDoesNotExist:
|
|
433
|
+
SkinDoseMapCalcSettings.objects.create()
|
|
434
|
+
|
|
435
|
+
enable_skin_dose_maps = SkinDoseMapCalcSettings.objects.values_list(
|
|
436
|
+
"enable_skin_dose_maps", flat=True
|
|
437
|
+
)[0]
|
|
438
|
+
calc_on_import = SkinDoseMapCalcSettings.objects.values_list(
|
|
439
|
+
"calc_on_import", flat=True
|
|
440
|
+
)[0]
|
|
441
|
+
if g.modality_type == "RF" and enable_skin_dose_maps and calc_on_import:
|
|
442
|
+
skin_maps_enabled = skin_dose_maps_enabled_for_xray_system(g)
|
|
443
|
+
if skin_maps_enabled:
|
|
444
|
+
run_in_background_with_limits(
|
|
445
|
+
make_skin_map,
|
|
446
|
+
"make_skin_map",
|
|
447
|
+
0,
|
|
448
|
+
{"make_skin_map": 1},
|
|
449
|
+
g.pk,
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
# Calculate summed total DAP and dose at RP for studies that have this study's patient ID, going back week_delta
|
|
453
|
+
# weeks in time from this study date. Only do this if activated in the fluoro alert settings (check whether
|
|
454
|
+
# HighDoseMetricAlertSettings.calc_accum_dose_over_delta_weeks_on_import is True).
|
|
455
|
+
if g.modality_type == "RF":
|
|
456
|
+
|
|
457
|
+
try:
|
|
458
|
+
HighDoseMetricAlertSettings.objects.get()
|
|
459
|
+
except ObjectDoesNotExist:
|
|
460
|
+
HighDoseMetricAlertSettings.objects.create()
|
|
461
|
+
|
|
462
|
+
week_delta = HighDoseMetricAlertSettings.objects.values_list(
|
|
463
|
+
"accum_dose_delta_weeks", flat=True
|
|
464
|
+
)[0]
|
|
465
|
+
calc_accum_dose_over_delta_weeks_on_import = (
|
|
466
|
+
HighDoseMetricAlertSettings.objects.values_list(
|
|
467
|
+
"calc_accum_dose_over_delta_weeks_on_import", flat=True
|
|
468
|
+
)[0]
|
|
469
|
+
)
|
|
470
|
+
if calc_accum_dose_over_delta_weeks_on_import:
|
|
471
|
+
|
|
472
|
+
all_rf_studies = GeneralStudyModuleAttr.objects.filter(
|
|
473
|
+
modality_type__exact="RF"
|
|
474
|
+
).all()
|
|
475
|
+
|
|
476
|
+
patient_id = g.patientmoduleattr_set.values_list("patient_id", flat=True)[0]
|
|
477
|
+
if patient_id:
|
|
478
|
+
study_date = g.study_date
|
|
479
|
+
oldest_date = study_date - timedelta(weeks=week_delta)
|
|
480
|
+
|
|
481
|
+
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
482
|
+
# The try and except parts of this code are here because some of the studies in my database didn't have the
|
|
483
|
+
# expected data in the related fields - not sure why. Perhaps an issue with the extractor routine?
|
|
484
|
+
try:
|
|
485
|
+
g.projectionxrayradiationdose_set.get().accumxraydose_set.all()
|
|
486
|
+
except ObjectDoesNotExist:
|
|
487
|
+
g.projectionxrayradiationdose_set.get().accumxraydose_set.create()
|
|
488
|
+
|
|
489
|
+
for (
|
|
490
|
+
accumxraydose
|
|
491
|
+
) in g.projectionxrayradiationdose_set.get().accumxraydose_set.all():
|
|
492
|
+
try:
|
|
493
|
+
accumxraydose.accumintegratedprojradiogdose_set.get()
|
|
494
|
+
except:
|
|
495
|
+
accumxraydose.accumintegratedprojradiogdose_set.create()
|
|
496
|
+
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
497
|
+
|
|
498
|
+
for (
|
|
499
|
+
accumxraydose
|
|
500
|
+
) in g.projectionxrayradiationdose_set.get().accumxraydose_set.all():
|
|
501
|
+
accum_int_proj_pk = (
|
|
502
|
+
accumxraydose.accumintegratedprojradiogdose_set.get().pk
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
accum_int_proj_to_update = (
|
|
506
|
+
AccumIntegratedProjRadiogDose.objects.get(pk=accum_int_proj_pk)
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
included_studies = all_rf_studies.filter(
|
|
510
|
+
patientmoduleattr__patient_id__exact=patient_id,
|
|
511
|
+
study_date__range=[oldest_date, study_date],
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
bulk_entries = []
|
|
515
|
+
for pk in included_studies.values_list("pk", flat=True):
|
|
516
|
+
if not PKsForSummedRFDoseStudiesInDeltaWeeks.objects.filter(
|
|
517
|
+
general_study_module_attributes_id__exact=g.pk
|
|
518
|
+
).filter(study_pk_in_delta_weeks__exact=pk):
|
|
519
|
+
new_entry = PKsForSummedRFDoseStudiesInDeltaWeeks()
|
|
520
|
+
new_entry.general_study_module_attributes_id = g.pk
|
|
521
|
+
new_entry.study_pk_in_delta_weeks = pk
|
|
522
|
+
bulk_entries.append(new_entry)
|
|
523
|
+
if len(bulk_entries):
|
|
524
|
+
PKsForSummedRFDoseStudiesInDeltaWeeks.objects.bulk_create(
|
|
525
|
+
bulk_entries
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
accum_totals = included_studies.aggregate(
|
|
529
|
+
Sum(
|
|
530
|
+
"projectionxrayradiationdose__accumxraydose__accumintegratedprojradiogdose__dose_area_product_total"
|
|
531
|
+
),
|
|
532
|
+
Sum(
|
|
533
|
+
"projectionxrayradiationdose__accumxraydose__accumintegratedprojradiogdose__dose_rp_total"
|
|
534
|
+
),
|
|
535
|
+
)
|
|
536
|
+
accum_int_proj_to_update.dose_area_product_total_over_delta_weeks = accum_totals[
|
|
537
|
+
"projectionxrayradiationdose__accumxraydose__accumintegratedprojradiogdose__dose_area_product_total__sum"
|
|
538
|
+
]
|
|
539
|
+
accum_int_proj_to_update.dose_rp_total_over_delta_weeks = accum_totals[
|
|
540
|
+
"projectionxrayradiationdose__accumxraydose__accumintegratedprojradiogdose__dose_rp_total__sum"
|
|
541
|
+
]
|
|
542
|
+
accum_int_proj_to_update.save()
|
|
543
|
+
populate_rf_delta_weeks_summary(g)
|
|
544
|
+
|
|
545
|
+
# Send an e-mail to all high dose alert recipients if this study is at or above threshold levels
|
|
546
|
+
send_alert_emails_ref = HighDoseMetricAlertSettings.objects.values_list(
|
|
547
|
+
"send_high_dose_metric_alert_emails_ref", flat=True
|
|
548
|
+
)[0]
|
|
549
|
+
send_alert_emails_skin = HighDoseMetricAlertSettings.objects.values_list(
|
|
550
|
+
"send_high_dose_metric_alert_emails_skin", flat=True
|
|
551
|
+
)[0]
|
|
552
|
+
if send_alert_emails_ref and not send_alert_emails_skin:
|
|
553
|
+
send_rf_high_dose_alert_email(g.pk)
|
|
554
|
+
|
|
555
|
+
# Add standard names
|
|
556
|
+
add_standard_names(g)
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
def _fix_toshiba_vhp(dataset):
|
|
560
|
+
"""
|
|
561
|
+
Replace forward slash in multi-value decimal string VR with back slash
|
|
562
|
+
:param dataset: DICOM dataset
|
|
563
|
+
:return: Repaired DICOM dataset
|
|
564
|
+
"""
|
|
565
|
+
|
|
566
|
+
for cont in dataset.ContentSequence:
|
|
567
|
+
if cont.ConceptNameCodeSequence[0].CodeMeaning == "CT Acquisition":
|
|
568
|
+
for cont2 in cont.ContentSequence:
|
|
569
|
+
if (
|
|
570
|
+
cont2.ConceptNameCodeSequence[0].CodeMeaning
|
|
571
|
+
== "Dose Reduce Parameters"
|
|
572
|
+
and cont2.ConceptNameCodeSequence[0].CodingSchemeDesignator
|
|
573
|
+
== "99TOSHIBA-TMSC"
|
|
574
|
+
):
|
|
575
|
+
for cont3 in cont2.ContentSequence:
|
|
576
|
+
if (
|
|
577
|
+
cont3.ConceptNameCodeSequence[0].CodeMeaning
|
|
578
|
+
== "Standard deviation of population"
|
|
579
|
+
):
|
|
580
|
+
try:
|
|
581
|
+
cont3.MeasuredValueSequence[0].NumericValue
|
|
582
|
+
except ValueError:
|
|
583
|
+
vhp_sd = dict.__getitem__(
|
|
584
|
+
cont3.MeasuredValueSequence[0], 0x40A30A
|
|
585
|
+
)
|
|
586
|
+
vhp_sd_value = vhp_sd.__getattribute__("value")
|
|
587
|
+
if "/" in vhp_sd_value:
|
|
588
|
+
vhp_sd_value = vhp_sd_value.replace("/", "\\")
|
|
589
|
+
new_vhp_sd = vhp_sd._replace(value=vhp_sd_value)
|
|
590
|
+
dict.__setitem__(
|
|
591
|
+
cont3.MeasuredValueSequence[0],
|
|
592
|
+
0x40A30A,
|
|
593
|
+
new_vhp_sd,
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
def rdsr(rdsr_file):
|
|
598
|
+
"""Extract radiation dose related data from DICOM Radiation SR objects.
|
|
599
|
+
|
|
600
|
+
:param rdsr_file: relative or absolute path to Radiation Dose Structured Report.
|
|
601
|
+
:type rdsr_file: str.
|
|
602
|
+
"""
|
|
603
|
+
|
|
604
|
+
try:
|
|
605
|
+
del_settings = DicomDeleteSettings.objects.get()
|
|
606
|
+
del_rdsr = del_settings.del_rdsr
|
|
607
|
+
except ObjectDoesNotExist:
|
|
608
|
+
del_rdsr = False
|
|
609
|
+
|
|
610
|
+
try:
|
|
611
|
+
dataset = pydicom.dcmread(rdsr_file)
|
|
612
|
+
except FileNotFoundError:
|
|
613
|
+
logger.warning(
|
|
614
|
+
f"rdsr.py not attempting to extract from {rdsr_file}, the file does not exist"
|
|
615
|
+
)
|
|
616
|
+
record_task_error_exit(
|
|
617
|
+
f"Not attempting to extract from {rdsr_file}, the file does not exist"
|
|
618
|
+
)
|
|
619
|
+
return 1
|
|
620
|
+
|
|
621
|
+
try:
|
|
622
|
+
dataset.decode()
|
|
623
|
+
except ValueError as e:
|
|
624
|
+
if "Invalid tag (0040, a30a): invalid literal for float()" in e.message:
|
|
625
|
+
_fix_toshiba_vhp(dataset)
|
|
626
|
+
dataset.decode()
|
|
627
|
+
|
|
628
|
+
if (
|
|
629
|
+
dataset.SOPClassUID
|
|
630
|
+
in (
|
|
631
|
+
"1.2.840.10008.5.1.4.1.1.88.67", # X-Ray Radiation Dose SR
|
|
632
|
+
"1.2.840.10008.5.1.4.1.1.88.22", # Enhanced SR
|
|
633
|
+
)
|
|
634
|
+
and dataset.ConceptNameCodeSequence[0].CodeValue
|
|
635
|
+
== "113701" # X-Ray Radiation Dose Report
|
|
636
|
+
):
|
|
637
|
+
logger.debug("rdsr.py extracting from {0}".format(rdsr_file))
|
|
638
|
+
_rdsr2db(dataset)
|
|
639
|
+
elif (
|
|
640
|
+
dataset.SOPClassUID == ("1.2.840.10008.5.1.4.1.1.88.22") # Enhanced SR
|
|
641
|
+
and dataset.ConceptNameCodeSequence[0].CodingSchemeDesignator
|
|
642
|
+
== "99SMS_RADSUM" # Siemens Arcadis
|
|
643
|
+
and dataset.ConceptNameCodeSequence[0].CodeValue == "C-10"
|
|
644
|
+
):
|
|
645
|
+
logger.debug("rdsr.py extracting from {0}".format(rdsr_file))
|
|
646
|
+
_rdsr2db(dataset)
|
|
647
|
+
elif (
|
|
648
|
+
dataset.SOPClassUID
|
|
649
|
+
== "1.2.840.10008.5.1.4.1.1.88.68" # Radiopharmaceutical Radiation Dose SR
|
|
650
|
+
and dataset.ConceptNameCodeSequence[0].CodeValue
|
|
651
|
+
== "113500" # Radiopharmaceutical Radiation Dose Report
|
|
652
|
+
):
|
|
653
|
+
logger.debug(f"rdsr.py extracting from {rdsr_file}")
|
|
654
|
+
_rdsr2db(dataset)
|
|
655
|
+
else:
|
|
656
|
+
logger.warning(
|
|
657
|
+
f"rdsr.py not attempting to extract from {rdsr_file}, not a radiation dose structured report"
|
|
658
|
+
)
|
|
659
|
+
record_task_error_exit(
|
|
660
|
+
f"Not attempting to extract from {rdsr_file}, not an rdsr"
|
|
661
|
+
)
|
|
662
|
+
return 1
|
|
663
|
+
|
|
664
|
+
if del_rdsr:
|
|
665
|
+
os.remove(rdsr_file)
|
|
666
|
+
|
|
667
|
+
return 0
|