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
openrem/remapp/views.py
CHANGED
|
@@ -1,1052 +1,1147 @@
|
|
|
1
|
-
# pylint: disable=too-many-lines
|
|
2
|
-
# OpenREM - Radiation Exposure Monitoring tools for the physicist
|
|
3
|
-
# Copyright (C) 2012,2013 The Royal Marsden NHS Foundation Trust
|
|
4
|
-
#
|
|
5
|
-
# This program is free software: you can redistribute it and/or modify
|
|
6
|
-
# it under the terms of the GNU General Public License as published by
|
|
7
|
-
# the Free Software Foundation, either version 3 of the License, or
|
|
8
|
-
# (at your option) any later version.
|
|
9
|
-
#
|
|
10
|
-
# This program is distributed in the hope that it will be useful,
|
|
11
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
-
# GNU General Public License for more details.
|
|
14
|
-
#
|
|
15
|
-
# Additional permission under section 7 of GPLv3:
|
|
16
|
-
# You shall not make any use of the name of The Royal Marsden NHS
|
|
17
|
-
# Foundation trust in connection with this Program in any press or
|
|
18
|
-
# other public announcement without the prior written consent of
|
|
19
|
-
# The Royal Marsden NHS Foundation Trust.
|
|
20
|
-
#
|
|
21
|
-
# You should have received a copy of the GNU General Public License
|
|
22
|
-
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
23
|
-
#
|
|
24
|
-
# 8/10/2014: DJP added new DX section and added DX to home page.
|
|
25
|
-
# 9/10/2014: DJP changed DX to CR
|
|
26
|
-
#
|
|
27
|
-
"""
|
|
28
|
-
.. module:: views.
|
|
29
|
-
:synopsis: Module to render appropriate content according to request.
|
|
30
|
-
|
|
31
|
-
.. moduleauthor:: Ed McDonagh
|
|
32
|
-
|
|
33
|
-
"""
|
|
34
|
-
|
|
35
|
-
import os
|
|
36
|
-
import gzip
|
|
37
|
-
import json
|
|
38
|
-
import logging
|
|
39
|
-
from datetime import datetime, timedelta
|
|
40
|
-
from decimal import Decimal
|
|
41
|
-
import pickle # nosec
|
|
42
|
-
from collections import OrderedDict
|
|
43
|
-
|
|
44
|
-
from django.db.models import (
|
|
45
|
-
Sum,
|
|
46
|
-
Q,
|
|
47
|
-
Max,
|
|
48
|
-
Count,
|
|
49
|
-
)
|
|
50
|
-
from django.contrib import messages
|
|
51
|
-
from django.contrib.auth import logout
|
|
52
|
-
from django.contrib.auth.decorators import login_required
|
|
53
|
-
from django.contrib.auth.models import Group
|
|
54
|
-
from django.core.exceptions import ObjectDoesNotExist
|
|
55
|
-
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
|
56
|
-
from django.http import HttpResponseRedirect, HttpResponse, JsonResponse
|
|
57
|
-
from django.shortcuts import render, redirect
|
|
58
|
-
from django.template.defaultfilters import register
|
|
59
|
-
from django.urls import reverse_lazy
|
|
60
|
-
from django.utils.translation import gettext as _
|
|
61
|
-
from django.views.decorators.csrf import csrf_exempt
|
|
62
|
-
from django.conf import settings
|
|
63
|
-
import numpy as np
|
|
64
|
-
|
|
65
|
-
from .forms import itemsPerPageForm
|
|
66
|
-
from .interface.mod_filters import (
|
|
67
|
-
RFSummaryListFilter,
|
|
68
|
-
RFFilterPlusStdNames,
|
|
69
|
-
RFFilterPlusPid,
|
|
70
|
-
RFFilterPlusPidPlusStdNames,
|
|
71
|
-
dx_acq_filter,
|
|
72
|
-
ct_acq_filter,
|
|
73
|
-
MGSummaryListFilter,
|
|
74
|
-
MGFilterPlusPid,
|
|
75
|
-
MGFilterPlusStdNames,
|
|
76
|
-
MGFilterPlusPidPlusStdNames,
|
|
77
|
-
nm_filter,
|
|
78
|
-
)
|
|
79
|
-
from .tools.make_skin_map import
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
from .
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
from .
|
|
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
|
-
def
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
"
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
-
f =
|
|
291
|
-
request.GET,
|
|
292
|
-
queryset=queryset,
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
f =
|
|
302
|
-
request.GET,
|
|
303
|
-
queryset=queryset,
|
|
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
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
.order_by(
|
|
432
|
-
"projectionxrayradiationdose__irradeventxraydata__irradiation_event_type"
|
|
433
|
-
)
|
|
434
|
-
)
|
|
435
|
-
|
|
436
|
-
stu_inc_totals.values_list(
|
|
437
|
-
"
|
|
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
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
.
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
.
|
|
806
|
-
)
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
"
|
|
816
|
-
"
|
|
817
|
-
"
|
|
818
|
-
"
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
if
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
)
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
if not
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
if
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1
|
+
# pylint: disable=too-many-lines
|
|
2
|
+
# OpenREM - Radiation Exposure Monitoring tools for the physicist
|
|
3
|
+
# Copyright (C) 2012,2013 The Royal Marsden NHS Foundation Trust
|
|
4
|
+
#
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
# (at your option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
# GNU General Public License for more details.
|
|
14
|
+
#
|
|
15
|
+
# Additional permission under section 7 of GPLv3:
|
|
16
|
+
# You shall not make any use of the name of The Royal Marsden NHS
|
|
17
|
+
# Foundation trust in connection with this Program in any press or
|
|
18
|
+
# other public announcement without the prior written consent of
|
|
19
|
+
# The Royal Marsden NHS Foundation Trust.
|
|
20
|
+
#
|
|
21
|
+
# You should have received a copy of the GNU General Public License
|
|
22
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
23
|
+
#
|
|
24
|
+
# 8/10/2014: DJP added new DX section and added DX to home page.
|
|
25
|
+
# 9/10/2014: DJP changed DX to CR
|
|
26
|
+
#
|
|
27
|
+
"""
|
|
28
|
+
.. module:: views.
|
|
29
|
+
:synopsis: Module to render appropriate content according to request.
|
|
30
|
+
|
|
31
|
+
.. moduleauthor:: Ed McDonagh
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
import os
|
|
36
|
+
import gzip
|
|
37
|
+
import json
|
|
38
|
+
import logging
|
|
39
|
+
from datetime import datetime, timedelta
|
|
40
|
+
from decimal import Decimal
|
|
41
|
+
import pickle # nosec
|
|
42
|
+
from collections import OrderedDict
|
|
43
|
+
|
|
44
|
+
from django.db.models import (
|
|
45
|
+
Sum,
|
|
46
|
+
Q,
|
|
47
|
+
Max,
|
|
48
|
+
Count,
|
|
49
|
+
)
|
|
50
|
+
from django.contrib import messages
|
|
51
|
+
from django.contrib.auth import logout
|
|
52
|
+
from django.contrib.auth.decorators import login_required
|
|
53
|
+
from django.contrib.auth.models import Group
|
|
54
|
+
from django.core.exceptions import ObjectDoesNotExist
|
|
55
|
+
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
|
56
|
+
from django.http import HttpResponseRedirect, HttpResponse, JsonResponse
|
|
57
|
+
from django.shortcuts import render, redirect
|
|
58
|
+
from django.template.defaultfilters import register
|
|
59
|
+
from django.urls import reverse_lazy
|
|
60
|
+
from django.utils.translation import gettext as _
|
|
61
|
+
from django.views.decorators.csrf import csrf_exempt
|
|
62
|
+
from django.conf import settings
|
|
63
|
+
import numpy as np
|
|
64
|
+
|
|
65
|
+
from .forms import itemsPerPageForm
|
|
66
|
+
from .interface.mod_filters import (
|
|
67
|
+
RFSummaryListFilter,
|
|
68
|
+
RFFilterPlusStdNames,
|
|
69
|
+
RFFilterPlusPid,
|
|
70
|
+
RFFilterPlusPidPlusStdNames,
|
|
71
|
+
dx_acq_filter,
|
|
72
|
+
ct_acq_filter,
|
|
73
|
+
MGSummaryListFilter,
|
|
74
|
+
MGFilterPlusPid,
|
|
75
|
+
MGFilterPlusStdNames,
|
|
76
|
+
MGFilterPlusPidPlusStdNames,
|
|
77
|
+
nm_filter,
|
|
78
|
+
)
|
|
79
|
+
from .tools.make_skin_map import (
|
|
80
|
+
make_skin_map,
|
|
81
|
+
skin_dose_maps_enabled_for_xray_system,
|
|
82
|
+
)
|
|
83
|
+
from .tools.background import run_in_background_with_limits
|
|
84
|
+
from .tools.check_standard_name_status import are_standard_names_enabled
|
|
85
|
+
from .views_charts_ct import (
|
|
86
|
+
generate_required_ct_charts_list,
|
|
87
|
+
ct_chart_form_processing,
|
|
88
|
+
)
|
|
89
|
+
from .views_charts_dx import (
|
|
90
|
+
generate_required_dx_charts_list,
|
|
91
|
+
dx_chart_form_processing,
|
|
92
|
+
)
|
|
93
|
+
from .views_charts_mg import (
|
|
94
|
+
generate_required_mg_charts_list,
|
|
95
|
+
mg_chart_form_processing,
|
|
96
|
+
)
|
|
97
|
+
from .views_charts_rf import (
|
|
98
|
+
generate_required_rf_charts_list,
|
|
99
|
+
rf_chart_form_processing,
|
|
100
|
+
)
|
|
101
|
+
from .views_charts_nm import nm_chart_form_processing, generate_required_nm_charts_list
|
|
102
|
+
from .models import (
|
|
103
|
+
GeneralStudyModuleAttr,
|
|
104
|
+
create_user_profile,
|
|
105
|
+
HighDoseMetricAlertSettings,
|
|
106
|
+
SkinDoseMapCalcSettings,
|
|
107
|
+
PatientIDSettings,
|
|
108
|
+
DicomDeleteSettings,
|
|
109
|
+
AdminTaskQuestions,
|
|
110
|
+
HomePageAdminSettings,
|
|
111
|
+
UpgradeStatus,
|
|
112
|
+
StandardNameSettings,
|
|
113
|
+
BackgroundTask,
|
|
114
|
+
)
|
|
115
|
+
from .version import __version__, __docs_version__, __skin_map_version__
|
|
116
|
+
|
|
117
|
+
os.environ["DJANGO_SETTINGS_MODULE"] = "openremproject.settings"
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
logger = logging.getLogger(__name__)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@register.filter
|
|
124
|
+
def multiply(value, arg):
|
|
125
|
+
"""
|
|
126
|
+
Return multiplication within Django templates
|
|
127
|
+
|
|
128
|
+
:param value: the value to multiply
|
|
129
|
+
:param arg: the second value to multiply
|
|
130
|
+
:return: the multiplication
|
|
131
|
+
"""
|
|
132
|
+
try:
|
|
133
|
+
value = float(value)
|
|
134
|
+
arg = float(arg)
|
|
135
|
+
return value * arg
|
|
136
|
+
except ValueError:
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def logout_page(request):
|
|
141
|
+
"""Log users out and re-direct them to the main page."""
|
|
142
|
+
logout(request)
|
|
143
|
+
return HttpResponseRedirect(reverse_lazy("home"))
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def update_items_per_page_form(request, user_profile):
|
|
147
|
+
# Obtain the number of items per page from the request
|
|
148
|
+
items_per_page_form = itemsPerPageForm(request.GET)
|
|
149
|
+
# check whether the form data is valid
|
|
150
|
+
if items_per_page_form.is_valid():
|
|
151
|
+
# Use the form data if the user clicked on the submit button
|
|
152
|
+
if "submit" in request.GET:
|
|
153
|
+
# process the data in form.cleaned_data as required
|
|
154
|
+
user_profile.itemsPerPage = items_per_page_form.cleaned_data["itemsPerPage"]
|
|
155
|
+
user_profile.save()
|
|
156
|
+
|
|
157
|
+
# If submit was not clicked then use the settings already stored in the user's profile
|
|
158
|
+
else:
|
|
159
|
+
form_data = {"itemsPerPage": user_profile.itemsPerPage}
|
|
160
|
+
items_per_page_form = itemsPerPageForm(form_data)
|
|
161
|
+
return items_per_page_form
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def get_or_create_user(request):
|
|
165
|
+
try:
|
|
166
|
+
# See if the user has plot settings in userprofile
|
|
167
|
+
user_profile = request.user.userprofile
|
|
168
|
+
except ObjectDoesNotExist:
|
|
169
|
+
# Create a default userprofile for the user if one doesn't exist
|
|
170
|
+
create_user_profile(sender=request.user, instance=request.user, created=True)
|
|
171
|
+
user_profile = request.user.userprofile
|
|
172
|
+
return user_profile
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def create_admin_info(request):
|
|
176
|
+
admin = {
|
|
177
|
+
"openremversion": __version__,
|
|
178
|
+
"docsversion": __docs_version__,
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
for group in request.user.groups.all():
|
|
182
|
+
admin[group.name] = True
|
|
183
|
+
return admin
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def create_paginated_study_list(request, f, user_profile):
|
|
187
|
+
paginator = Paginator(f.qs, user_profile.itemsPerPage)
|
|
188
|
+
page = request.GET.get("page")
|
|
189
|
+
try:
|
|
190
|
+
study_list = paginator.page(page)
|
|
191
|
+
except PageNotAnInteger:
|
|
192
|
+
study_list = paginator.page(1)
|
|
193
|
+
except EmptyPage:
|
|
194
|
+
study_list = paginator.page(paginator.num_pages)
|
|
195
|
+
return study_list
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def generate_return_structure(request, f):
|
|
199
|
+
user_profile = get_or_create_user(request)
|
|
200
|
+
items_per_page_form = update_items_per_page_form(request, user_profile)
|
|
201
|
+
admin = create_admin_info(request)
|
|
202
|
+
study_list = create_paginated_study_list(request, f, user_profile)
|
|
203
|
+
enable_standard_names = are_standard_names_enabled()
|
|
204
|
+
return_structure = {
|
|
205
|
+
"filter": f,
|
|
206
|
+
"study_list": study_list,
|
|
207
|
+
"admin": admin,
|
|
208
|
+
"itemsPerPageForm": items_per_page_form,
|
|
209
|
+
"showStandardNames": enable_standard_names,
|
|
210
|
+
}
|
|
211
|
+
return user_profile, return_structure
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@login_required
|
|
215
|
+
def dx_summary_list_filter(request):
|
|
216
|
+
"""Obtain data for radiographic summary view."""
|
|
217
|
+
pid = bool(request.user.groups.filter(name="pidgroup"))
|
|
218
|
+
f = dx_acq_filter(request.GET, pid=pid)
|
|
219
|
+
|
|
220
|
+
user_profile, return_structure = generate_return_structure(request, f)
|
|
221
|
+
chart_options_form = dx_chart_form_processing(request, user_profile)
|
|
222
|
+
return_structure["chartOptionsForm"] = chart_options_form
|
|
223
|
+
|
|
224
|
+
if user_profile.plotCharts:
|
|
225
|
+
return_structure["required_charts"] = generate_required_dx_charts_list(
|
|
226
|
+
user_profile
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
return render(request, "remapp/dxfiltered.html", return_structure)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@login_required
|
|
233
|
+
def dx_detail_view(request, pk=None):
|
|
234
|
+
"""Detail view for a DX study."""
|
|
235
|
+
|
|
236
|
+
try:
|
|
237
|
+
study = GeneralStudyModuleAttr.objects.get(pk=pk)
|
|
238
|
+
except:
|
|
239
|
+
messages.error(request, "That study was not found")
|
|
240
|
+
return redirect(reverse_lazy("dx_summary_list_filter"))
|
|
241
|
+
|
|
242
|
+
admin = create_admin_info(request)
|
|
243
|
+
enable_standard_names = are_standard_names_enabled()
|
|
244
|
+
|
|
245
|
+
projection_set = study.projectionxrayradiationdose_set.get()
|
|
246
|
+
events_all = projection_set.irradeventxraydata_set.select_related(
|
|
247
|
+
"anatomical_structure",
|
|
248
|
+
"laterality",
|
|
249
|
+
"target_region",
|
|
250
|
+
"image_view",
|
|
251
|
+
"patient_orientation_modifier_cid",
|
|
252
|
+
"acquisition_plane",
|
|
253
|
+
).all()
|
|
254
|
+
|
|
255
|
+
accum_set = projection_set.accumxraydose_set.all()
|
|
256
|
+
# accum_integrated = projection_set.accumxraydose_set.get().accumintegratedprojradiogdose_set.get()
|
|
257
|
+
|
|
258
|
+
return render(
|
|
259
|
+
request,
|
|
260
|
+
"remapp/dxdetail.html",
|
|
261
|
+
{
|
|
262
|
+
"generalstudymoduleattr": study,
|
|
263
|
+
"admin": admin,
|
|
264
|
+
"projection_set": projection_set,
|
|
265
|
+
"events_all": events_all,
|
|
266
|
+
"accum_set": accum_set,
|
|
267
|
+
"showStandardNames": enable_standard_names,
|
|
268
|
+
},
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
@login_required
|
|
273
|
+
def rf_summary_list_filter(request):
|
|
274
|
+
"""Obtain data for radiographic summary view."""
|
|
275
|
+
|
|
276
|
+
enable_standard_names = are_standard_names_enabled()
|
|
277
|
+
queryset = (
|
|
278
|
+
GeneralStudyModuleAttr.objects.filter(modality_type="RF")
|
|
279
|
+
.order_by("-study_date", "-study_time")
|
|
280
|
+
.distinct()
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
if request.user.groups.filter(name="pidgroup"):
|
|
284
|
+
if enable_standard_names:
|
|
285
|
+
f = RFFilterPlusPidPlusStdNames(
|
|
286
|
+
request.GET,
|
|
287
|
+
queryset=queryset,
|
|
288
|
+
)
|
|
289
|
+
else:
|
|
290
|
+
f = RFFilterPlusPid(
|
|
291
|
+
request.GET,
|
|
292
|
+
queryset=queryset,
|
|
293
|
+
)
|
|
294
|
+
else:
|
|
295
|
+
if enable_standard_names:
|
|
296
|
+
f = RFFilterPlusStdNames(
|
|
297
|
+
request.GET,
|
|
298
|
+
queryset=queryset,
|
|
299
|
+
)
|
|
300
|
+
else:
|
|
301
|
+
f = RFSummaryListFilter(
|
|
302
|
+
request.GET,
|
|
303
|
+
queryset=queryset,
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
user_profile, return_structure = generate_return_structure(request, f)
|
|
307
|
+
chart_options_form = rf_chart_form_processing(request, user_profile)
|
|
308
|
+
|
|
309
|
+
# Import total DAP and total dose at reference point alert levels. Create with default values if not found.
|
|
310
|
+
try:
|
|
311
|
+
HighDoseMetricAlertSettings.objects.get()
|
|
312
|
+
except ObjectDoesNotExist:
|
|
313
|
+
HighDoseMetricAlertSettings.objects.create()
|
|
314
|
+
alert_levels = HighDoseMetricAlertSettings.objects.values(
|
|
315
|
+
"show_accum_dose_over_delta_weeks",
|
|
316
|
+
"alert_total_dap_rf",
|
|
317
|
+
"alert_total_rp_dose_rf",
|
|
318
|
+
"accum_dose_delta_weeks",
|
|
319
|
+
)[0]
|
|
320
|
+
|
|
321
|
+
return_structure["chartOptionsForm"] = chart_options_form
|
|
322
|
+
return_structure["alertLevels"] = alert_levels
|
|
323
|
+
|
|
324
|
+
if user_profile.plotCharts:
|
|
325
|
+
return_structure["required_charts"] = generate_required_rf_charts_list(
|
|
326
|
+
user_profile
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
return render(request, "remapp/rffiltered.html", return_structure)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
@login_required
|
|
333
|
+
def rf_detail_view(request, pk=None):
|
|
334
|
+
"""Detail view for an RF study."""
|
|
335
|
+
|
|
336
|
+
enable_standard_names = are_standard_names_enabled()
|
|
337
|
+
|
|
338
|
+
try:
|
|
339
|
+
study = GeneralStudyModuleAttr.objects.get(pk=pk)
|
|
340
|
+
except ObjectDoesNotExist:
|
|
341
|
+
messages.error(request, "That study was not found")
|
|
342
|
+
return redirect(reverse_lazy("rf_summary_list_filter"))
|
|
343
|
+
|
|
344
|
+
# get the totals
|
|
345
|
+
irradiation_types = [("Fluoroscopy",), ("Acquisition",)]
|
|
346
|
+
fluoro_dap_total = Decimal(0)
|
|
347
|
+
fluoro_rp_total = Decimal(0)
|
|
348
|
+
acq_dap_total = Decimal(0)
|
|
349
|
+
acq_rp_total = Decimal(0)
|
|
350
|
+
stu_dose_totals = [(0, 0), (0, 0)]
|
|
351
|
+
stu_time_totals = [0, 0]
|
|
352
|
+
total_dap = 0
|
|
353
|
+
total_dose = 0
|
|
354
|
+
# Iterate over the planes (for bi-plane systems, for single plane systems there is only one)
|
|
355
|
+
projection_xray_dose_set = study.projectionxrayradiationdose_set.get()
|
|
356
|
+
accumxraydose_set_all_planes = (
|
|
357
|
+
projection_xray_dose_set.accumxraydose_set.select_related(
|
|
358
|
+
"acquisition_plane"
|
|
359
|
+
).all()
|
|
360
|
+
)
|
|
361
|
+
events_all = projection_xray_dose_set.irradeventxraydata_set.select_related(
|
|
362
|
+
"irradiation_event_type",
|
|
363
|
+
"patient_table_relationship_cid",
|
|
364
|
+
"patient_orientation_cid",
|
|
365
|
+
"patient_orientation_modifier_cid",
|
|
366
|
+
"acquisition_plane",
|
|
367
|
+
).all()
|
|
368
|
+
|
|
369
|
+
for dose_ds in accumxraydose_set_all_planes:
|
|
370
|
+
accum_dose_ds = dose_ds.accumprojxraydose_set.get()
|
|
371
|
+
try:
|
|
372
|
+
fluoro_dap_total += accum_dose_ds.fluoro_gym2_to_cgycm2()
|
|
373
|
+
except TypeError:
|
|
374
|
+
pass
|
|
375
|
+
try:
|
|
376
|
+
fluoro_rp_total += accum_dose_ds.fluoro_dose_rp_total
|
|
377
|
+
except TypeError:
|
|
378
|
+
pass
|
|
379
|
+
try:
|
|
380
|
+
acq_dap_total += accum_dose_ds.acq_gym2_to_cgycm2()
|
|
381
|
+
except TypeError:
|
|
382
|
+
pass
|
|
383
|
+
try:
|
|
384
|
+
acq_rp_total += accum_dose_ds.acquisition_dose_rp_total
|
|
385
|
+
except TypeError:
|
|
386
|
+
pass
|
|
387
|
+
stu_dose_totals[0] = (fluoro_dap_total, fluoro_rp_total)
|
|
388
|
+
stu_dose_totals[1] = (acq_dap_total, acq_rp_total)
|
|
389
|
+
try:
|
|
390
|
+
stu_time_totals[0] = stu_time_totals[0] + accum_dose_ds.total_fluoro_time
|
|
391
|
+
except TypeError:
|
|
392
|
+
pass
|
|
393
|
+
try:
|
|
394
|
+
stu_time_totals[1] = (
|
|
395
|
+
stu_time_totals[1] + accum_dose_ds.total_acquisition_time
|
|
396
|
+
)
|
|
397
|
+
except TypeError:
|
|
398
|
+
pass
|
|
399
|
+
accum_integrated = (
|
|
400
|
+
accum_dose_ds.accumulated_xray_dose.accumintegratedprojradiogdose_set.get()
|
|
401
|
+
)
|
|
402
|
+
try:
|
|
403
|
+
total_dap = total_dap + accum_integrated.dose_area_product_total
|
|
404
|
+
except TypeError:
|
|
405
|
+
pass
|
|
406
|
+
try:
|
|
407
|
+
total_dose = total_dose + accum_dose_ds.dose_rp_total
|
|
408
|
+
except TypeError:
|
|
409
|
+
pass
|
|
410
|
+
|
|
411
|
+
# get info for different Acquisition Types
|
|
412
|
+
stu_inc_totals = ( # pylint: disable=line-too-long
|
|
413
|
+
GeneralStudyModuleAttr.objects.filter(
|
|
414
|
+
pk=pk,
|
|
415
|
+
projectionxrayradiationdose__irradeventxraydata__irradiation_event_type__code_meaning__contains="Acquisition",
|
|
416
|
+
)
|
|
417
|
+
.annotate(
|
|
418
|
+
sum_dap=Sum(
|
|
419
|
+
"projectionxrayradiationdose__irradeventxraydata__dose_area_product"
|
|
420
|
+
)
|
|
421
|
+
* 1000000,
|
|
422
|
+
sum_dose_rp=Sum(
|
|
423
|
+
"projectionxrayradiationdose__irradeventxraydata__irradeventxraysourcedata__dose_rp"
|
|
424
|
+
),
|
|
425
|
+
)
|
|
426
|
+
.order_by(
|
|
427
|
+
"projectionxrayradiationdose__irradeventxraydata__irradiation_event_type"
|
|
428
|
+
)
|
|
429
|
+
)
|
|
430
|
+
stu_dose_totals.extend(
|
|
431
|
+
stu_inc_totals.values_list("sum_dap", "sum_dose_rp").order_by(
|
|
432
|
+
"projectionxrayradiationdose__irradeventxraydata__irradiation_event_type"
|
|
433
|
+
)
|
|
434
|
+
)
|
|
435
|
+
acq_irr_types = (
|
|
436
|
+
stu_inc_totals.values_list(
|
|
437
|
+
"projectionxrayradiationdose__irradeventxraydata__irradiation_event_type__code_meaning"
|
|
438
|
+
)
|
|
439
|
+
.order_by(
|
|
440
|
+
"projectionxrayradiationdose__irradeventxraydata__irradiation_event_type"
|
|
441
|
+
)
|
|
442
|
+
.distinct()
|
|
443
|
+
)
|
|
444
|
+
# stu_time_totals = [None] * len(stu_irr_types)
|
|
445
|
+
for _, irr_type in enumerate(acq_irr_types):
|
|
446
|
+
stu_time_totals.append( # pylint: disable=line-too-long
|
|
447
|
+
list(
|
|
448
|
+
GeneralStudyModuleAttr.objects.filter(
|
|
449
|
+
pk=pk,
|
|
450
|
+
projectionxrayradiationdose__irradeventxraydata__irradiation_event_type__code_meaning=irr_type[
|
|
451
|
+
0
|
|
452
|
+
],
|
|
453
|
+
)
|
|
454
|
+
.aggregate(
|
|
455
|
+
Sum(
|
|
456
|
+
"projectionxrayradiationdose__irradeventxraydata__irradeventxraysourcedata__irradiation_duration"
|
|
457
|
+
)
|
|
458
|
+
)
|
|
459
|
+
.values()
|
|
460
|
+
)[0]
|
|
461
|
+
)
|
|
462
|
+
irradiation_types.extend([("- " + acq_type[0],) for acq_type in acq_irr_types])
|
|
463
|
+
|
|
464
|
+
# Add the study totals
|
|
465
|
+
irradiation_types.append(("Total",))
|
|
466
|
+
stu_dose_totals.append((total_dap * 1000000, total_dose))
|
|
467
|
+
# does total duration (summed over fluoroscopy and acquisitions) means something?
|
|
468
|
+
stu_time_totals.append(stu_time_totals[0] + stu_time_totals[1])
|
|
469
|
+
|
|
470
|
+
study_totals = np.column_stack(
|
|
471
|
+
(irradiation_types, stu_dose_totals, stu_time_totals)
|
|
472
|
+
).tolist()
|
|
473
|
+
|
|
474
|
+
try:
|
|
475
|
+
SkinDoseMapCalcSettings.objects.get()
|
|
476
|
+
except ObjectDoesNotExist:
|
|
477
|
+
SkinDoseMapCalcSettings.objects.create()
|
|
478
|
+
|
|
479
|
+
# Import total DAP and total dose at reference point alert levels. Create with default values if not found.
|
|
480
|
+
try:
|
|
481
|
+
HighDoseMetricAlertSettings.objects.get()
|
|
482
|
+
except ObjectDoesNotExist:
|
|
483
|
+
HighDoseMetricAlertSettings.objects.create()
|
|
484
|
+
alert_levels = HighDoseMetricAlertSettings.objects.values(
|
|
485
|
+
"show_accum_dose_over_delta_weeks",
|
|
486
|
+
"alert_total_dap_rf",
|
|
487
|
+
"alert_total_rp_dose_rf",
|
|
488
|
+
"accum_dose_delta_weeks",
|
|
489
|
+
)[0]
|
|
490
|
+
|
|
491
|
+
# Obtain the studies that are within delta weeks if needed
|
|
492
|
+
if alert_levels["show_accum_dose_over_delta_weeks"]:
|
|
493
|
+
patient_id = study.patientmoduleattr_set.values_list("patient_id", flat=True)[0]
|
|
494
|
+
if patient_id:
|
|
495
|
+
study_date = study.study_date
|
|
496
|
+
week_delta = HighDoseMetricAlertSettings.objects.values_list(
|
|
497
|
+
"accum_dose_delta_weeks", flat=True
|
|
498
|
+
)[0]
|
|
499
|
+
oldest_date = study_date - timedelta(weeks=week_delta)
|
|
500
|
+
included_studies = GeneralStudyModuleAttr.objects.filter(
|
|
501
|
+
modality_type="RF",
|
|
502
|
+
patientmoduleattr__patient_id=patient_id,
|
|
503
|
+
study_date__range=[oldest_date, study_date],
|
|
504
|
+
)
|
|
505
|
+
else:
|
|
506
|
+
included_studies = None
|
|
507
|
+
else:
|
|
508
|
+
included_studies = None
|
|
509
|
+
|
|
510
|
+
admin = create_admin_info(request)
|
|
511
|
+
admin["enable_skin_dose_maps"] = SkinDoseMapCalcSettings.objects.values_list(
|
|
512
|
+
"enable_skin_dose_maps", flat=True
|
|
513
|
+
)[0]
|
|
514
|
+
|
|
515
|
+
for group in request.user.groups.all():
|
|
516
|
+
admin[group.name] = True
|
|
517
|
+
|
|
518
|
+
return render(
|
|
519
|
+
request,
|
|
520
|
+
"remapp/rfdetail.html",
|
|
521
|
+
{
|
|
522
|
+
"generalstudymoduleattr": study,
|
|
523
|
+
"admin": admin,
|
|
524
|
+
"study_totals": study_totals,
|
|
525
|
+
"projection_xray_dose_set": projection_xray_dose_set,
|
|
526
|
+
"accumxraydose_set_all_planes": accumxraydose_set_all_planes,
|
|
527
|
+
"events_all": events_all,
|
|
528
|
+
"alert_levels": alert_levels,
|
|
529
|
+
"studies_in_week_delta": included_studies,
|
|
530
|
+
"showStandardNames": enable_standard_names,
|
|
531
|
+
},
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
@login_required
|
|
536
|
+
def rf_detail_view_skin_map(request, pk=None):
|
|
537
|
+
"""View to calculate a skin dose map."""
|
|
538
|
+
try:
|
|
539
|
+
GeneralStudyModuleAttr.objects.get(pk=pk)
|
|
540
|
+
except ObjectDoesNotExist:
|
|
541
|
+
messages.error(request, "That study was not found")
|
|
542
|
+
return redirect(reverse_lazy("rf_summary_list_filter"))
|
|
543
|
+
|
|
544
|
+
return_structure = {}
|
|
545
|
+
|
|
546
|
+
# Get the study
|
|
547
|
+
study = GeneralStudyModuleAttr.objects.get(pk=pk)
|
|
548
|
+
|
|
549
|
+
# Find the latest task corresponding to this study. This is done in two
|
|
550
|
+
# stages because using .latest("task_type") on an empty queryset throws
|
|
551
|
+
# a DoesNotExist error
|
|
552
|
+
matching_latest_task = None
|
|
553
|
+
matching_tasks = BackgroundTask.objects.filter(
|
|
554
|
+
task_type="make_skin_map",
|
|
555
|
+
info__contains=pk,
|
|
556
|
+
)
|
|
557
|
+
if matching_tasks:
|
|
558
|
+
matching_latest_task = matching_tasks.latest("task_type")
|
|
559
|
+
|
|
560
|
+
latest_task_failed = False
|
|
561
|
+
if matching_latest_task:
|
|
562
|
+
if (
|
|
563
|
+
matching_latest_task.error is not None
|
|
564
|
+
): # Avoid TypeError in next line if error is None
|
|
565
|
+
if (
|
|
566
|
+
matching_latest_task.completed_successfully is False
|
|
567
|
+
and "failed" in matching_latest_task.error
|
|
568
|
+
):
|
|
569
|
+
latest_task_failed = True
|
|
570
|
+
|
|
571
|
+
# Check if skin dose maps are enabled for the x-ray system used for the study
|
|
572
|
+
skin_maps_enabled = skin_dose_maps_enabled_for_xray_system(study)
|
|
573
|
+
if skin_maps_enabled is False:
|
|
574
|
+
# Some code to return something that says they are disabled for this system
|
|
575
|
+
return_structure["disabled_skin_maps"] = True
|
|
576
|
+
|
|
577
|
+
elif (
|
|
578
|
+
latest_task_failed
|
|
579
|
+
and "force_recalculation" not in request.resolver_match.url_name
|
|
580
|
+
):
|
|
581
|
+
if (
|
|
582
|
+
matching_latest_task.completed_successfully is False
|
|
583
|
+
and "failed" in matching_latest_task.error
|
|
584
|
+
):
|
|
585
|
+
return_structure["skin_map_calculation_failed"] = True
|
|
586
|
+
|
|
587
|
+
# Find out if there is a task running to re-calculate the skin dose map for this study
|
|
588
|
+
matching_ongoing_task = BackgroundTask.objects.filter(
|
|
589
|
+
task_type="make_skin_map", info__contains=pk, complete=False
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
# Set the skin_map_progress and in_progress entries if there is a match to a running task.
|
|
593
|
+
if matching_ongoing_task.count() != 0:
|
|
594
|
+
return_structure["skin_map_progress"] = (
|
|
595
|
+
matching_ongoing_task.values_list("info", flat=True)[0].split(
|
|
596
|
+
"irradiation ", 1
|
|
597
|
+
)[1]
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
return_structure["in_progress"] = True
|
|
601
|
+
|
|
602
|
+
else:
|
|
603
|
+
# Check to see if there is already a skin map pickle with the same study ID.
|
|
604
|
+
try:
|
|
605
|
+
study_date = GeneralStudyModuleAttr.objects.get(pk=pk).study_date
|
|
606
|
+
if study_date:
|
|
607
|
+
skin_map_path = os.path.join(
|
|
608
|
+
settings.MEDIA_ROOT,
|
|
609
|
+
"skin_maps",
|
|
610
|
+
"{0:0>4}".format(study_date.year),
|
|
611
|
+
"{0:0>2}".format(study_date.month),
|
|
612
|
+
"{0:0>2}".format(study_date.day),
|
|
613
|
+
"skin_map_" + str(pk) + ".p",
|
|
614
|
+
)
|
|
615
|
+
else:
|
|
616
|
+
skin_map_path = os.path.join(
|
|
617
|
+
settings.MEDIA_ROOT, "skin_maps", "skin_map_" + str(pk) + ".p"
|
|
618
|
+
)
|
|
619
|
+
except:
|
|
620
|
+
skin_map_path = os.path.join(
|
|
621
|
+
settings.MEDIA_ROOT, "skin_maps", "skin_map_" + str(pk) + ".p"
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
# If patient weight is missing from the database then db_pat_mass will be undefined
|
|
625
|
+
try:
|
|
626
|
+
db_pat_mass = float(
|
|
627
|
+
GeneralStudyModuleAttr.objects.get(pk=pk)
|
|
628
|
+
.patientstudymoduleattr_set.get()
|
|
629
|
+
.patient_weight
|
|
630
|
+
)
|
|
631
|
+
except (ValueError, TypeError):
|
|
632
|
+
db_pat_mass = 73.2
|
|
633
|
+
if not db_pat_mass:
|
|
634
|
+
db_pat_mass = 73.2
|
|
635
|
+
|
|
636
|
+
# If patient weight is missing from the database then db_pat_mass will be undefined
|
|
637
|
+
try:
|
|
638
|
+
db_pat_height = (
|
|
639
|
+
float(
|
|
640
|
+
GeneralStudyModuleAttr.objects.get(pk=pk)
|
|
641
|
+
.patientstudymoduleattr_set.get()
|
|
642
|
+
.patient_size
|
|
643
|
+
)
|
|
644
|
+
* 100
|
|
645
|
+
)
|
|
646
|
+
except (ValueError, TypeError):
|
|
647
|
+
db_pat_height = 178.6
|
|
648
|
+
if not db_pat_height:
|
|
649
|
+
db_pat_height = 178.6
|
|
650
|
+
|
|
651
|
+
loaded_existing_data = False
|
|
652
|
+
pat_mass_unchanged = False
|
|
653
|
+
pat_height_unchanged = False
|
|
654
|
+
|
|
655
|
+
return_structure["in_progress"] = False
|
|
656
|
+
|
|
657
|
+
if os.path.exists(skin_map_path):
|
|
658
|
+
with gzip.open(skin_map_path, "rb") as f:
|
|
659
|
+
existing_skin_map_data = pickle.load(f) # nosec
|
|
660
|
+
try:
|
|
661
|
+
if existing_skin_map_data["skin_map_version"] == __skin_map_version__:
|
|
662
|
+
# Round the float values to 1 decimal place and convert to string before comparing
|
|
663
|
+
if str(round(existing_skin_map_data["patient_height"], 1)) == str(
|
|
664
|
+
round(db_pat_height, 1)
|
|
665
|
+
):
|
|
666
|
+
pat_height_unchanged = True
|
|
667
|
+
|
|
668
|
+
# Round the float values to 1 decimal place and convert to string before comparing
|
|
669
|
+
if str(round(existing_skin_map_data["patient_mass"], 1)) == str(
|
|
670
|
+
round(db_pat_mass, 1)
|
|
671
|
+
):
|
|
672
|
+
pat_mass_unchanged = True
|
|
673
|
+
|
|
674
|
+
if pat_height_unchanged and pat_mass_unchanged:
|
|
675
|
+
return_structure = existing_skin_map_data
|
|
676
|
+
return_structure["in_progress"] = False
|
|
677
|
+
loaded_existing_data = True
|
|
678
|
+
except KeyError:
|
|
679
|
+
pass
|
|
680
|
+
|
|
681
|
+
if not loaded_existing_data:
|
|
682
|
+
# Check to see if there is already a background task running to calculate a skin dose map for this study.
|
|
683
|
+
# Need to have a Django query that matches the following SQL:
|
|
684
|
+
# SELECT info
|
|
685
|
+
# FROM remapp_backgroundtask
|
|
686
|
+
# WHERE task_type='make_skin_map' [just make_skin_map tasks]
|
|
687
|
+
# AND info LIKE '%1880069%' [info contains the pk of the study, 1880069 in this case]
|
|
688
|
+
# AND complete=FALSE; [it is not yet complete]
|
|
689
|
+
#
|
|
690
|
+
# If there are no rows returned from the above query then there is not already a job being run to calculate
|
|
691
|
+
# the skin dose map for this study, and we can go ahead and create a background task to run it.
|
|
692
|
+
|
|
693
|
+
# The following line is equivalent to the above SQL:
|
|
694
|
+
matching_ongoing_task = BackgroundTask.objects.filter(
|
|
695
|
+
task_type="make_skin_map", info__contains=pk, complete=False
|
|
696
|
+
)
|
|
697
|
+
|
|
698
|
+
# Only run make_skin_map if matching_ongoing_task is empty.
|
|
699
|
+
if matching_ongoing_task.count() == 0:
|
|
700
|
+
run_in_background_with_limits(
|
|
701
|
+
make_skin_map,
|
|
702
|
+
"make_skin_map",
|
|
703
|
+
0,
|
|
704
|
+
{"make_skin_map": 1},
|
|
705
|
+
pk,
|
|
706
|
+
)
|
|
707
|
+
else:
|
|
708
|
+
return_structure["skin_map_progress"] = (
|
|
709
|
+
matching_ongoing_task.values_list("info", flat=True)[0].split(
|
|
710
|
+
"irradiation ", 1
|
|
711
|
+
)[1]
|
|
712
|
+
)
|
|
713
|
+
|
|
714
|
+
return_structure["in_progress"] = True
|
|
715
|
+
|
|
716
|
+
return_structure["primary_key"] = pk
|
|
717
|
+
return JsonResponse(return_structure, safe=False)
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
@login_required
|
|
721
|
+
def nm_summary_list_filter(request):
|
|
722
|
+
"""Obtain data for NM summary view."""
|
|
723
|
+
pid = bool(request.user.groups.filter(name="pidgroup"))
|
|
724
|
+
f = nm_filter(request.GET, pid=pid)
|
|
725
|
+
|
|
726
|
+
user_profile, return_structure = generate_return_structure(request, f)
|
|
727
|
+
chart_options_form = nm_chart_form_processing(request, user_profile)
|
|
728
|
+
return_structure["chartOptionsForm"] = chart_options_form
|
|
729
|
+
|
|
730
|
+
if user_profile.plotCharts:
|
|
731
|
+
return_structure["required_charts"] = generate_required_nm_charts_list(
|
|
732
|
+
user_profile
|
|
733
|
+
)
|
|
734
|
+
|
|
735
|
+
return render(request, "remapp/nmfiltered.html", return_structure)
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
@login_required
|
|
739
|
+
def nm_detail_view(request, pk=None):
|
|
740
|
+
"""Detail view for a NM study."""
|
|
741
|
+
try:
|
|
742
|
+
study = GeneralStudyModuleAttr.objects.get(pk=pk)
|
|
743
|
+
except ObjectDoesNotExist:
|
|
744
|
+
messages.error(request, "That study was not found")
|
|
745
|
+
return redirect(reverse_lazy("nm_summary_list_filter"))
|
|
746
|
+
|
|
747
|
+
associated_ct = GeneralStudyModuleAttr.objects.filter(
|
|
748
|
+
Q(study_instance_uid=study.study_instance_uid) & Q(modality_type="CT")
|
|
749
|
+
).first()
|
|
750
|
+
|
|
751
|
+
admin = create_admin_info(request)
|
|
752
|
+
enable_standard_names = are_standard_names_enabled()
|
|
753
|
+
|
|
754
|
+
return render(
|
|
755
|
+
request,
|
|
756
|
+
"remapp/nmdetail.html",
|
|
757
|
+
{
|
|
758
|
+
"generalstudymoduleattr": study,
|
|
759
|
+
"admin": admin,
|
|
760
|
+
"associated_ct": associated_ct,
|
|
761
|
+
"showStandardNames": enable_standard_names,
|
|
762
|
+
},
|
|
763
|
+
)
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
@login_required
|
|
767
|
+
def ct_summary_list_filter(request):
|
|
768
|
+
"""Obtain data for CT summary view."""
|
|
769
|
+
pid = bool(request.user.groups.filter(name="pidgroup"))
|
|
770
|
+
f = ct_acq_filter(request.GET, pid=pid)
|
|
771
|
+
|
|
772
|
+
user_profile, return_structure = generate_return_structure(request, f)
|
|
773
|
+
chart_options_form = ct_chart_form_processing(request, user_profile)
|
|
774
|
+
return_structure["chartOptionsForm"] = chart_options_form
|
|
775
|
+
|
|
776
|
+
if user_profile.plotCharts:
|
|
777
|
+
return_structure["required_charts"] = generate_required_ct_charts_list(
|
|
778
|
+
user_profile
|
|
779
|
+
)
|
|
780
|
+
|
|
781
|
+
return render(request, "remapp/ctfiltered.html", return_structure)
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
@login_required
|
|
785
|
+
def ct_detail_view(request, pk=None):
|
|
786
|
+
"""Detail view for a CT study."""
|
|
787
|
+
|
|
788
|
+
enable_standard_names = are_standard_names_enabled()
|
|
789
|
+
|
|
790
|
+
try:
|
|
791
|
+
study = GeneralStudyModuleAttr.objects.get(pk=pk)
|
|
792
|
+
except ObjectDoesNotExist:
|
|
793
|
+
messages.error(request, "That study was not found")
|
|
794
|
+
return redirect(reverse_lazy("ct_summary_list_filter"))
|
|
795
|
+
|
|
796
|
+
events_all = (
|
|
797
|
+
study.ctradiationdose_set.get()
|
|
798
|
+
.ctirradiationeventdata_set.select_related(
|
|
799
|
+
"ct_acquisition_type", "ctdiw_phantom_type"
|
|
800
|
+
)
|
|
801
|
+
.order_by("pk")
|
|
802
|
+
)
|
|
803
|
+
|
|
804
|
+
associated_nm = GeneralStudyModuleAttr.objects.filter(
|
|
805
|
+
Q(study_instance_uid=study.study_instance_uid) & Q(modality_type="NM")
|
|
806
|
+
).first()
|
|
807
|
+
|
|
808
|
+
admin = create_admin_info(request)
|
|
809
|
+
|
|
810
|
+
return render(
|
|
811
|
+
request,
|
|
812
|
+
"remapp/ctdetail.html",
|
|
813
|
+
{
|
|
814
|
+
"generalstudymoduleattr": study,
|
|
815
|
+
"admin": admin,
|
|
816
|
+
"events_all": events_all,
|
|
817
|
+
"associated_nm": associated_nm,
|
|
818
|
+
"showStandardNames": enable_standard_names,
|
|
819
|
+
},
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
@login_required
|
|
824
|
+
def mg_summary_list_filter(request):
|
|
825
|
+
"""Mammography data for summary view."""
|
|
826
|
+
|
|
827
|
+
enable_standard_names = are_standard_names_enabled()
|
|
828
|
+
filter_data = request.GET.copy()
|
|
829
|
+
if "page" in filter_data:
|
|
830
|
+
del filter_data["page"]
|
|
831
|
+
|
|
832
|
+
queryset = (
|
|
833
|
+
GeneralStudyModuleAttr.objects.filter(modality_type="MG")
|
|
834
|
+
.order_by("-study_date", "-study_time")
|
|
835
|
+
.distinct()
|
|
836
|
+
)
|
|
837
|
+
|
|
838
|
+
if request.user.groups.filter(name="pidgroup"):
|
|
839
|
+
if enable_standard_names:
|
|
840
|
+
f = MGFilterPlusPidPlusStdNames(
|
|
841
|
+
filter_data,
|
|
842
|
+
queryset=queryset,
|
|
843
|
+
)
|
|
844
|
+
else:
|
|
845
|
+
f = MGFilterPlusPid(
|
|
846
|
+
filter_data,
|
|
847
|
+
queryset=queryset,
|
|
848
|
+
)
|
|
849
|
+
else:
|
|
850
|
+
if enable_standard_names:
|
|
851
|
+
f = MGFilterPlusStdNames(
|
|
852
|
+
filter_data,
|
|
853
|
+
queryset=queryset,
|
|
854
|
+
)
|
|
855
|
+
else:
|
|
856
|
+
f = MGSummaryListFilter(
|
|
857
|
+
filter_data,
|
|
858
|
+
queryset=queryset,
|
|
859
|
+
)
|
|
860
|
+
|
|
861
|
+
user_profile, return_structure = generate_return_structure(request, f)
|
|
862
|
+
chart_options_form = mg_chart_form_processing(request, user_profile)
|
|
863
|
+
return_structure["chartOptionsForm"] = chart_options_form
|
|
864
|
+
|
|
865
|
+
if user_profile.plotCharts:
|
|
866
|
+
return_structure["required_charts"] = generate_required_mg_charts_list(
|
|
867
|
+
user_profile
|
|
868
|
+
)
|
|
869
|
+
|
|
870
|
+
return render(request, "remapp/mgfiltered.html", return_structure)
|
|
871
|
+
|
|
872
|
+
|
|
873
|
+
@login_required
|
|
874
|
+
def mg_detail_view(request, pk=None):
|
|
875
|
+
"""Detail view for a CT study."""
|
|
876
|
+
|
|
877
|
+
enable_standard_names = are_standard_names_enabled()
|
|
878
|
+
|
|
879
|
+
try:
|
|
880
|
+
study = GeneralStudyModuleAttr.objects.get(pk=pk)
|
|
881
|
+
except:
|
|
882
|
+
messages.error(request, "That study was not found")
|
|
883
|
+
return redirect(reverse_lazy("mg_summary_list_filter"))
|
|
884
|
+
|
|
885
|
+
admin = create_admin_info(request)
|
|
886
|
+
|
|
887
|
+
projection_xray_dose_set = study.projectionxrayradiationdose_set.get()
|
|
888
|
+
accum_mammo_set = (
|
|
889
|
+
projection_xray_dose_set.accumxraydose_set.get()
|
|
890
|
+
.accummammographyxraydose_set.select_related("laterality")
|
|
891
|
+
.all()
|
|
892
|
+
)
|
|
893
|
+
events_all = projection_xray_dose_set.irradeventxraydata_set.select_related(
|
|
894
|
+
"laterality", "image_view"
|
|
895
|
+
).all()
|
|
896
|
+
|
|
897
|
+
return render(
|
|
898
|
+
request,
|
|
899
|
+
"remapp/mgdetail.html",
|
|
900
|
+
{
|
|
901
|
+
"generalstudymoduleattr": study,
|
|
902
|
+
"admin": admin,
|
|
903
|
+
"projection_xray_dose_set": projection_xray_dose_set,
|
|
904
|
+
"accum_mammo_set": accum_mammo_set,
|
|
905
|
+
"events_all": events_all,
|
|
906
|
+
"showStandardNames": enable_standard_names,
|
|
907
|
+
},
|
|
908
|
+
)
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
def openrem_home(request):
|
|
912
|
+
try:
|
|
913
|
+
HomePageAdminSettings.objects.get()
|
|
914
|
+
except ObjectDoesNotExist:
|
|
915
|
+
HomePageAdminSettings.objects.create()
|
|
916
|
+
|
|
917
|
+
test_dicom_store_settings = DicomDeleteSettings.objects.all()
|
|
918
|
+
if not test_dicom_store_settings:
|
|
919
|
+
DicomDeleteSettings.objects.create()
|
|
920
|
+
|
|
921
|
+
if not Group.objects.filter(name="viewgroup"):
|
|
922
|
+
vg = Group(name="viewgroup")
|
|
923
|
+
vg.save()
|
|
924
|
+
if not Group.objects.filter(name="exportgroup"):
|
|
925
|
+
eg = Group(name="exportgroup")
|
|
926
|
+
eg.save()
|
|
927
|
+
if not Group.objects.filter(name="admingroup"):
|
|
928
|
+
ag = Group(name="admingroup")
|
|
929
|
+
ag.save()
|
|
930
|
+
if not Group.objects.filter(name="pidgroup"):
|
|
931
|
+
pg = Group(name="pidgroup")
|
|
932
|
+
pg.save()
|
|
933
|
+
if not Group.objects.filter(name="importsizegroup"):
|
|
934
|
+
sg = Group(name="importsizegroup")
|
|
935
|
+
sg.save()
|
|
936
|
+
if not Group.objects.filter(name="importqrgroup"):
|
|
937
|
+
qg = Group(name="importqrgroup")
|
|
938
|
+
qg.save()
|
|
939
|
+
|
|
940
|
+
id_settings = PatientIDSettings.objects.all()
|
|
941
|
+
if not id_settings:
|
|
942
|
+
PatientIDSettings.objects.create()
|
|
943
|
+
|
|
944
|
+
users_in_groups = {"any": False, "admin": False}
|
|
945
|
+
for g in Group.objects.all():
|
|
946
|
+
if Group.objects.get(name=g).user_set.all():
|
|
947
|
+
users_in_groups["any"] = True
|
|
948
|
+
if g.name == "admingroup":
|
|
949
|
+
users_in_groups["admin"] = True
|
|
950
|
+
|
|
951
|
+
try:
|
|
952
|
+
# See if the user has plot settings in userprofile
|
|
953
|
+
user_profile = request.user.userprofile
|
|
954
|
+
except (ObjectDoesNotExist, AttributeError):
|
|
955
|
+
# Attribute error needed for AnonymousUser, who doesn't have a userprofile attribute
|
|
956
|
+
if request.user.is_authenticated:
|
|
957
|
+
# Create a default userprofile for the user if one doesn't exist
|
|
958
|
+
create_user_profile(
|
|
959
|
+
sender=request.user, instance=request.user, created=True
|
|
960
|
+
)
|
|
961
|
+
user_profile = request.user.userprofile
|
|
962
|
+
|
|
963
|
+
allstudies = GeneralStudyModuleAttr.objects.all()
|
|
964
|
+
study_counts = allstudies.aggregate(
|
|
965
|
+
all_count=Count("pk"),
|
|
966
|
+
ct_count=Count("pk", filter=Q(modality_type="CT")),
|
|
967
|
+
rf_count=Count("pk", filter=Q(modality_type="RF")),
|
|
968
|
+
mg_count=Count("pk", filter=Q(modality_type="MG")),
|
|
969
|
+
nm_count=Count("pk", filter=Q(modality_type="NM")),
|
|
970
|
+
dx_count=Count("pk", filter=Q(modality_type__in=["DX", "CR", "PX"])),
|
|
971
|
+
)
|
|
972
|
+
|
|
973
|
+
modalities = OrderedDict()
|
|
974
|
+
if study_counts["ct_count"]:
|
|
975
|
+
modalities["CT"] = {"name": _("CT"), "count": study_counts["ct_count"]}
|
|
976
|
+
if study_counts["rf_count"]:
|
|
977
|
+
modalities["RF"] = {"name": _("Fluoroscopy"), "count": study_counts["rf_count"]}
|
|
978
|
+
if study_counts["mg_count"]:
|
|
979
|
+
modalities["MG"] = {"name": _("Mammography"), "count": study_counts["mg_count"]}
|
|
980
|
+
if study_counts["dx_count"]:
|
|
981
|
+
modalities["DX"] = {"name": _("Radiography"), "count": study_counts["dx_count"]}
|
|
982
|
+
if study_counts["nm_count"]:
|
|
983
|
+
modalities["NM"] = {
|
|
984
|
+
"name": _("Nuclear Medicine"),
|
|
985
|
+
"count": study_counts["nm_count"],
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
homedata = {"total": study_counts["all_count"]}
|
|
989
|
+
|
|
990
|
+
# Determine whether to calculate workload settings
|
|
991
|
+
display_workload_stats = HomePageAdminSettings.objects.values_list(
|
|
992
|
+
"enable_workload_stats", flat=True
|
|
993
|
+
)[0]
|
|
994
|
+
home_config = {"display_workload_stats": display_workload_stats}
|
|
995
|
+
if display_workload_stats:
|
|
996
|
+
if request.user.is_authenticated:
|
|
997
|
+
home_config["day_delta_a"] = user_profile.summaryWorkloadDaysA
|
|
998
|
+
home_config["day_delta_b"] = user_profile.summaryWorkloadDaysB
|
|
999
|
+
else:
|
|
1000
|
+
home_config["day_delta_a"] = 7
|
|
1001
|
+
home_config["day_delta_b"] = 28
|
|
1002
|
+
|
|
1003
|
+
admin = dict(openremversion=__version__, docsversion=__docs_version__)
|
|
1004
|
+
|
|
1005
|
+
for group in request.user.groups.all():
|
|
1006
|
+
admin[group.name] = True
|
|
1007
|
+
|
|
1008
|
+
admin_questions = {}
|
|
1009
|
+
admin_questions_true = False
|
|
1010
|
+
if request.user.groups.filter(name="admingroup"):
|
|
1011
|
+
not_patient_indicator_question = (
|
|
1012
|
+
AdminTaskQuestions.get_solo().ask_revert_to_074_question
|
|
1013
|
+
)
|
|
1014
|
+
admin_questions["not_patient_indicator_question"] = (
|
|
1015
|
+
not_patient_indicator_question
|
|
1016
|
+
)
|
|
1017
|
+
# if any(value for value in admin_questions.itervalues()):
|
|
1018
|
+
# admin_questions_true = True # Don't know why this doesn't work
|
|
1019
|
+
if not_patient_indicator_question:
|
|
1020
|
+
admin_questions_true = True # Doing this instead
|
|
1021
|
+
|
|
1022
|
+
upgrade_status = UpgradeStatus.get_solo()
|
|
1023
|
+
migration_complete = upgrade_status.from_0_9_1_summary_fields
|
|
1024
|
+
if not migration_complete and homedata["total"] == 0:
|
|
1025
|
+
upgrade_status.from_0_9_1_summary_fields = True
|
|
1026
|
+
upgrade_status.save()
|
|
1027
|
+
migration_complete = True
|
|
1028
|
+
|
|
1029
|
+
# from remapp.tools.send_high_dose_alert_emails import send_rf_high_dose_alert_email
|
|
1030
|
+
# send_rf_high_dose_alert_email(881397)
|
|
1031
|
+
# send_rf_high_dose_alert_email(417973)
|
|
1032
|
+
# # Send a test e-mail
|
|
1033
|
+
# from django.core.mail import send_mail
|
|
1034
|
+
# from openremproject import settings
|
|
1035
|
+
# from remapp.models import HighDoseMetricAlertSettings
|
|
1036
|
+
# from django.contrib.auth.models import User
|
|
1037
|
+
#
|
|
1038
|
+
# try:
|
|
1039
|
+
# HighDoseMetricAlertSettings.objects.get()
|
|
1040
|
+
# except ObjectDoesNotExist:
|
|
1041
|
+
# HighDoseMetricAlertSettings.objects.create()
|
|
1042
|
+
#
|
|
1043
|
+
# send_alert_emails = HighDoseMetricAlertSettings.objects.values_list(
|
|
1044
|
+
# 'send_high_dose_metric_alert_emails', flat=True
|
|
1045
|
+
# )[0]
|
|
1046
|
+
# if send_alert_emails:
|
|
1047
|
+
# recipients = User.objects.filter(
|
|
1048
|
+
# highdosemetricalertrecipients__receive_high_dose_metric_alerts=True
|
|
1049
|
+
# ).values_list('email', flat=True)
|
|
1050
|
+
# send_mail('OpenREM high dose alert test',
|
|
1051
|
+
# 'This is a test for high dose alert e-mails from OpenREM',
|
|
1052
|
+
# settings.EMAIL_DOSE_ALERT_SENDER,
|
|
1053
|
+
# recipients,
|
|
1054
|
+
# fail_silently=False)
|
|
1055
|
+
# # End of sending a test e-mail
|
|
1056
|
+
|
|
1057
|
+
return render(
|
|
1058
|
+
request,
|
|
1059
|
+
"remapp/home.html",
|
|
1060
|
+
{
|
|
1061
|
+
"homedata": homedata,
|
|
1062
|
+
"admin": admin,
|
|
1063
|
+
"users_in_groups": users_in_groups,
|
|
1064
|
+
"admin_questions": admin_questions,
|
|
1065
|
+
"admin_questions_true": admin_questions_true,
|
|
1066
|
+
"modalities": modalities,
|
|
1067
|
+
"home_config": home_config,
|
|
1068
|
+
"migration_complete": migration_complete,
|
|
1069
|
+
},
|
|
1070
|
+
)
|
|
1071
|
+
|
|
1072
|
+
|
|
1073
|
+
@csrf_exempt
|
|
1074
|
+
def update_latest_studies(request):
|
|
1075
|
+
"""
|
|
1076
|
+
AJAX function to calculate the latest studies for each display name for a particular modality.
|
|
1077
|
+
|
|
1078
|
+
:param request: Request object
|
|
1079
|
+
:return: HTML table of modalities
|
|
1080
|
+
"""
|
|
1081
|
+
if request.is_ajax():
|
|
1082
|
+
data = request.POST
|
|
1083
|
+
modality = data.get("modality")
|
|
1084
|
+
if modality == "DX":
|
|
1085
|
+
studies = GeneralStudyModuleAttr.objects.filter(
|
|
1086
|
+
Q(modality_type__in=["DX", "CR", "PX"])
|
|
1087
|
+
).all()
|
|
1088
|
+
else:
|
|
1089
|
+
studies = GeneralStudyModuleAttr.objects.filter(
|
|
1090
|
+
modality_type=modality
|
|
1091
|
+
).all()
|
|
1092
|
+
|
|
1093
|
+
today = datetime.now()
|
|
1094
|
+
study_data = (
|
|
1095
|
+
studies.values(
|
|
1096
|
+
"generalequipmentmoduleattr__unique_equipment_name__display_name"
|
|
1097
|
+
)
|
|
1098
|
+
.annotate(
|
|
1099
|
+
num_studies=Count("pk"),
|
|
1100
|
+
latest_entry_date_time=Max("test_date_time"),
|
|
1101
|
+
# timedelta=ExpressionWrapper(today - F("latest_entry_date_time"), output_field=DurationField()),
|
|
1102
|
+
)
|
|
1103
|
+
.order_by("-latest_entry_date_time")
|
|
1104
|
+
)
|
|
1105
|
+
|
|
1106
|
+
display_workload_stats = HomePageAdminSettings.objects.values_list(
|
|
1107
|
+
"enable_workload_stats", flat=True
|
|
1108
|
+
)[0]
|
|
1109
|
+
if request.user.is_authenticated:
|
|
1110
|
+
day_delta_a = request.user.userprofile.summaryWorkloadDaysA
|
|
1111
|
+
day_delta_b = request.user.userprofile.summaryWorkloadDaysB
|
|
1112
|
+
else:
|
|
1113
|
+
day_delta_a = 7
|
|
1114
|
+
day_delta_b = 28
|
|
1115
|
+
|
|
1116
|
+
date_a = today.date() - timedelta(days=day_delta_a)
|
|
1117
|
+
date_b = today.date() - timedelta(days=day_delta_b)
|
|
1118
|
+
if display_workload_stats:
|
|
1119
|
+
study_data = study_data.annotate(
|
|
1120
|
+
studies_since_delta_a=Count("pk", filter=Q(test_date_time__gte=date_a)),
|
|
1121
|
+
studies_since_delta_b=Count("pk", filter=Q(test_date_time__gte=date_b)),
|
|
1122
|
+
).order_by("-latest_entry_date_time")
|
|
1123
|
+
|
|
1124
|
+
admin = {}
|
|
1125
|
+
for group in request.user.groups.all():
|
|
1126
|
+
admin[group.name] = True
|
|
1127
|
+
|
|
1128
|
+
template = "remapp/home-list-modalities.html"
|
|
1129
|
+
|
|
1130
|
+
home_config = {
|
|
1131
|
+
"display_workload_stats": display_workload_stats,
|
|
1132
|
+
"day_delta_a": day_delta_a,
|
|
1133
|
+
"day_delta_b": day_delta_b,
|
|
1134
|
+
"date_a": datetime.strftime(date_a, "%Y-%m-%d"),
|
|
1135
|
+
"date_b": datetime.strftime(date_b, "%Y-%m-%d"),
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
return render(
|
|
1139
|
+
request,
|
|
1140
|
+
template,
|
|
1141
|
+
{
|
|
1142
|
+
"study_data": study_data,
|
|
1143
|
+
"modality": modality.lower(),
|
|
1144
|
+
"home_config": home_config,
|
|
1145
|
+
"admin": admin,
|
|
1146
|
+
},
|
|
1147
|
+
)
|