From 79f5ff0b3ae69223c6fcfcf56f0a52cfd756470f Mon Sep 17 00:00:00 2001 From: Pradeeban Kathiravelu Date: Fri, 21 May 2021 10:50:00 -0400 Subject: [PATCH 01/46] Find with checks to re-run when clear_storage accesses the same folder at once --- modules/meta-extraction/MetadataExtractor.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/meta-extraction/MetadataExtractor.py b/modules/meta-extraction/MetadataExtractor.py index 7180abb..9afb39b 100644 --- a/modules/meta-extraction/MetadataExtractor.py +++ b/modules/meta-extraction/MetadataExtractor.py @@ -167,8 +167,17 @@ def extract_metadata(): t_start = time.time() logging.info('Started the metadata extraction at: %s', str(datetime.datetime.now())) EXTRACTION_RUNNING = True + FIND_NOT_RUNNING = True - series_string = subprocess.check_output("find -maxdepth 3 -mindepth 3 -type d", shell=True) + # Checks to ensure the clear_storage thread does not cause a missing file exception randomly, when run in parallel. + while FIND_NOT_RUNNING: + FIND_NOT_RUNNING = False + try: + series_string = subprocess.check_output("find -maxdepth 3 -mindepth 3 -type d", shell=True) + except: + FIND_NOT_RUNNING = True + time.sleep(10) # sleep for 10 seconds before rerunning the find + series = series_string.splitlines() logging.info('Number of series: %s', len(series)) this_iteration = list(set(series) - (set(processed_series_but_yet_to_delete) | set(processed_and_deleted_series))) From b486bcfae55e5c7935ff40ae61fd0c2cedb7fd56 Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Thu, 27 May 2021 19:33:42 +0530 Subject: [PATCH 02/46] Frontend Added --- modules/frontend/server.py | 13 + modules/frontend/static/css/base.css | 48 ++++ modules/frontend/static/css/tmp.css | 4 + modules/frontend/static/images/img1.png | Bin 0 -> 114776 bytes modules/frontend/templates/home.html | 17 ++ modules/frontend/templates/pngHome.html | 361 ++++++++++++++++++++++++ 6 files changed, 443 insertions(+) create mode 100644 modules/frontend/server.py create mode 100644 modules/frontend/static/css/base.css create mode 100644 modules/frontend/static/css/tmp.css create mode 100644 modules/frontend/static/images/img1.png create mode 100644 modules/frontend/templates/home.html create mode 100644 modules/frontend/templates/pngHome.html diff --git a/modules/frontend/server.py b/modules/frontend/server.py new file mode 100644 index 0000000..3284081 --- /dev/null +++ b/modules/frontend/server.py @@ -0,0 +1,13 @@ +from flask import Flask, flash, request, redirect, url_for, render_template, send_file +import os + +PEOPLE_FOLDER = os.path.join('static','styles') +app = Flask(__name__) + +@app.route("/") +def hello_world(): + return render_template('pngHome.html') + +#JUST DO IT!!! +if __name__=="__main__": + app.run(host="0.0.0.0", port="9000",threaded=False) \ No newline at end of file diff --git a/modules/frontend/static/css/base.css b/modules/frontend/static/css/base.css new file mode 100644 index 0000000..40952ec --- /dev/null +++ b/modules/frontend/static/css/base.css @@ -0,0 +1,48 @@ +.nav-item { + margin: 8px; + padding: 12px; + background: #2c3e50; + box-shadow: 2px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 10px; +} + +/* .nav-link { + color: #ffffff; + font-size: 24px; +} */ + +.nav-item a { + text-decoration: none; + color: #ffffff; + font-size: 18px; +} + +.nav-item a:hover { + color: aqua; +} + +.nav-item button { + color: #ffffff; +} + +.item { + padding: 12px; + margin: 10px; +} + +.backdrop { + /* position: absolute; */ + background-image: url("../images/img1.png"); + background-repeat: no-repeat; + background-size: cover; + height: 95vh; +} + +.row { + padding-top: 10px; +} + +.text { + color: white; + font-size: 18px; +} diff --git a/modules/frontend/static/css/tmp.css b/modules/frontend/static/css/tmp.css new file mode 100644 index 0000000..03a555f --- /dev/null +++ b/modules/frontend/static/css/tmp.css @@ -0,0 +1,4 @@ +body { + background: black; + color: #ffffff; +} diff --git a/modules/frontend/static/images/img1.png b/modules/frontend/static/images/img1.png new file mode 100644 index 0000000000000000000000000000000000000000..d142ee9947c8442b4f9b5ef6bc751375af816567 GIT binary patch literal 114776 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 UdDI1nCF2Rg + + + + + + + LOL + + +

LOL !!

+ + diff --git a/modules/frontend/templates/pngHome.html b/modules/frontend/templates/pngHome.html new file mode 100644 index 0000000..1bffc87 --- /dev/null +++ b/modules/frontend/templates/pngHome.html @@ -0,0 +1,361 @@ + + + + + + + PNG Extraction + + + + + + + +
+
+
+
+
+

+ PNG Extraction +

+
+
+
+
+
+
+

+ Edit Configurations +

+
+
+
+
DICOM Home Folder
+
+
+ +
+
+ +
+
+
Output Folder
+
+
+ +
+
+ +
+
+
Depth
+
+
+ +
+
+ +
+
+
Split into chunks
+
+
+ +
+
+ +
+
+
Use Processes
+
+
+ +
+
+ +
+
+
Flattened To Level
+
+
+ +
+
+ +
+
+
DICOM Home Folder
+
+
+ +
+
+ +
+
+
To 16-bit
+
+
+ +
+
+ +
+
+
Print Images
+
+
+ +
+
+ +
+
+
Common Headers Only
+
+
+ +
+
+ +
+
+
Send Email
+
+
+ +
+
+ +
+
+
Email
+
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + From 111f0be0a11acc506a81986cdae0591fca1ceb13 Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Thu, 27 May 2021 19:34:41 +0530 Subject: [PATCH 03/46] Frontend Added --- modules/frontend/static/css/tmp.css | 4 ---- modules/frontend/templates/home.html | 17 ----------------- 2 files changed, 21 deletions(-) delete mode 100644 modules/frontend/static/css/tmp.css delete mode 100644 modules/frontend/templates/home.html diff --git a/modules/frontend/static/css/tmp.css b/modules/frontend/static/css/tmp.css deleted file mode 100644 index 03a555f..0000000 --- a/modules/frontend/static/css/tmp.css +++ /dev/null @@ -1,4 +0,0 @@ -body { - background: black; - color: #ffffff; -} diff --git a/modules/frontend/templates/home.html b/modules/frontend/templates/home.html deleted file mode 100644 index 197cc9b..0000000 --- a/modules/frontend/templates/home.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - LOL - - -

LOL !!

- - From 21e1c459b6837776b0cedffda7e10c1d5396795c Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Sat, 29 May 2021 10:47:07 +0530 Subject: [PATCH 04/46] PNG Extraction Frontend --- modules/frontend/server.py | 25 ++++++++++++-- modules/frontend/templates/pngHome.html | 45 +++++++++++++++---------- 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/modules/frontend/server.py b/modules/frontend/server.py index 3284081..57e369a 100644 --- a/modules/frontend/server.py +++ b/modules/frontend/server.py @@ -4,10 +4,29 @@ PEOPLE_FOLDER = os.path.join('static','styles') app = Flask(__name__) -@app.route("/") -def hello_world(): +@app.route("/", methods=['GET']) +def index(): return render_template('pngHome.html') +config_values = {} + +@app.route('/process', methods=['POST']) +def getValues(): + if request.method =='POST': + config_values["dcmFolder"] = request.form['DICOMFolder'] + config_values["outputFolder"] = request.form['outputFolder'] + config_values["depth"] = request.form['depth'] + config_values["chunks"] = request.form['chunks'] + config_values["useProcess"] = request.form['useProcess'] + config_values["level"] = request.form['level'] + config_values["16Bit"] = request.form['16Bit'] + config_values["printImages"] = request.form['printImages'] + config_values["headers"] = request.form['headers'] + config_values["sendEmail"] = request.form['sendEmail'] + config_values["email"] = request.form['email'] + print(config_values) + return config_values + #JUST DO IT!!! if __name__=="__main__": - app.run(host="0.0.0.0", port="9000",threaded=False) \ No newline at end of file + app.run( port="9000") \ No newline at end of file diff --git a/modules/frontend/templates/pngHome.html b/modules/frontend/templates/pngHome.html index 1bffc87..fc73f37 100644 --- a/modules/frontend/templates/pngHome.html +++ b/modules/frontend/templates/pngHome.html @@ -143,7 +143,11 @@

-
+
DICOM Home Folder
- --> +
@@ -181,13 +191,19 @@

Output Folder
- --> +
@@ -214,6 +230,7 @@

@@ -227,6 +244,7 @@

@@ -240,24 +258,12 @@

-
-
-
DICOM Home Folder
-
-
- -
-
-
To 16-bit
@@ -266,6 +272,7 @@

@@ -279,6 +286,7 @@

@@ -292,6 +300,7 @@

@@ -305,6 +314,7 @@

@@ -318,6 +328,7 @@

@@ -327,7 +338,7 @@

- +
Date: Sun, 30 May 2021 10:58:55 +0530 Subject: [PATCH 06/46] Update README.md --- modules/frontend/README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/modules/frontend/README.md b/modules/frontend/README.md index e69de29..2893f47 100644 --- a/modules/frontend/README.md +++ b/modules/frontend/README.md @@ -0,0 +1,14 @@ +# A Frontend Framework for Niffler +=================================== + +Here we have created a frontend module for Niffler for ease of acccess of it's modules. + +This module is using **Flask** as an engine to run all the frontend. +*Make sure you have installed Flask from requirements.txt file.* + +Run server.py file by navigating into frontend directory and running: +`python server.py` or `python3 server.py` + +Then navigate to `localhost:9000` to view your Niffler modules. + +*Currently PNG extraction Frontend is developed and it can take values from frontend and can be passed into backend* From b0bd7ead4f070667bcce57d1f69b1cada1b31bb0 Mon Sep 17 00:00:00 2001 From: Nishchal Singi <71981858+Nishchal-007@users.noreply.github.com> Date: Sun, 30 May 2021 10:59:08 +0530 Subject: [PATCH 07/46] Update README.md --- modules/frontend/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/frontend/README.md b/modules/frontend/README.md index 2893f47..4aa934a 100644 --- a/modules/frontend/README.md +++ b/modules/frontend/README.md @@ -1,5 +1,4 @@ # A Frontend Framework for Niffler -=================================== Here we have created a frontend module for Niffler for ease of acccess of it's modules. From 06b100da3910e9b36b2dba9681c9ac8dc53ebe96 Mon Sep 17 00:00:00 2001 From: Nishchal Singi <71981858+Nishchal-007@users.noreply.github.com> Date: Sun, 30 May 2021 11:04:10 +0530 Subject: [PATCH 08/46] Update README.md --- modules/frontend/README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/frontend/README.md b/modules/frontend/README.md index 4aa934a..1e55781 100644 --- a/modules/frontend/README.md +++ b/modules/frontend/README.md @@ -1,13 +1,18 @@ # A Frontend Framework for Niffler Here we have created a frontend module for Niffler for ease of acccess of it's modules. - This module is using **Flask** as an engine to run all the frontend. -*Make sure you have installed Flask from requirements.txt file.* -Run server.py file by navigating into frontend directory and running: +**Make sure you have installed Flask from requirements.txt file.** + +## Steps for running Frontend Module + +1. Run server.py file by navigating into frontend directory and running: `python server.py` or `python3 server.py` -Then navigate to `localhost:9000` to view your Niffler modules. +2. Then navigate to `localhost:9000` to view your Niffler modules. + *Currently PNG extraction Frontend is developed and it can take values from frontend and can be passed into backend* + +**NOTE: `__init__.py` file is important as it serves the frontend directory as a package, so the values can be accessed from other modules also** From 1d232073a21e3da1c77bfd3df890ef78d49591eb Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Sun, 30 May 2021 11:06:05 +0530 Subject: [PATCH 09/46] Added requirements.txt --- modules/frontend/requirements.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 modules/frontend/requirements.txt diff --git a/modules/frontend/requirements.txt b/modules/frontend/requirements.txt new file mode 100644 index 0000000..5cf8eda --- /dev/null +++ b/modules/frontend/requirements.txt @@ -0,0 +1,15 @@ +click==8.0.1 +colorama==0.4.4 +Flask==2.0.1 +itsdangerous==2.0.1 +Jinja2==3.0.1 +MarkupSafe==2.0.1 +numpy==1.20.3 +pandas==1.2.4 +Pillow==8.2.0 +pydicom==2.1.2 +python-dateutil==2.8.1 +pytz==2021.1 +six==1.16.0 +sqlparse==0.4.1 +Werkzeug==2.0.1 From 6a68adb87798ea1bc386e17c039b32b15e3f9a1e Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Wed, 9 Jun 2021 19:09:21 +0530 Subject: [PATCH 10/46] PNG-Extraction Done --- modules/frontend/server.py | 15 +- modules/frontend/static/css/base.css | 65 ++- modules/frontend/static/images/home.png | Bin 0 -> 209297 bytes modules/frontend/templates/Home.html | 168 +++++++ modules/frontend/templates/pngHome.html | 51 +- modules/png-extraction/ImageExtractor.py | 451 +++++++++++------- .../__pycache__/ImageExtractor.cpython-38.pyc | Bin 0 -> 11493 bytes modules/png-extraction/config.json | 22 +- 8 files changed, 564 insertions(+), 208 deletions(-) create mode 100644 modules/frontend/static/images/home.png create mode 100644 modules/frontend/templates/Home.html create mode 100644 modules/png-extraction/__pycache__/ImageExtractor.cpython-38.pyc diff --git a/modules/frontend/server.py b/modules/frontend/server.py index 9a5aaa8..ff865ea 100644 --- a/modules/frontend/server.py +++ b/modules/frontend/server.py @@ -6,6 +6,10 @@ @app.route("/", methods=['GET']) def index(): + return render_template('home.html') + +@app.route("/png-extraction", methods = ['GET']) +def PNG_Extraction(): return render_template('pngHome.html') config_values = {} @@ -24,9 +28,14 @@ def getPNGValues(): config_values["headers"] = request.form['headers'] config_values["sendEmail"] = request.form['sendEmail'] config_values["email"] = request.form['email'] - print(config_values) - return config_values - return render_template('pngHome.html', values = config_values) + + if(len(config_values) > 0): + import sys + sys.path.append("../png-extraction/") + import ImageExtractor + lt = ImageExtractor.initialize_Values(config_values) + return render_template('pngHome.html', logs = lt) + return render_template('pngHome.html') #JUST DO IT!!! if __name__=="__main__": diff --git a/modules/frontend/static/css/base.css b/modules/frontend/static/css/base.css index 40952ec..84ed14e 100644 --- a/modules/frontend/static/css/base.css +++ b/modules/frontend/static/css/base.css @@ -1,3 +1,8 @@ +body, +html { + height: 100%; +} + .nav-item { margin: 8px; padding: 12px; @@ -30,12 +35,70 @@ margin: 10px; } +/* HomePage Styles */ + +.homeBackground { + background-image: url("../images/home.png"); + height: 100%; + width: 100%; + background-position: center; + background-repeat: no-repeat; + background-size: cover; + position: absolute; +} + +.mainHeading { + background-color: rgba(52, 152, 219, 0.6); + box-shadow: 4px 4px 4px rgba(0, 0, 0, 0.65); + border-radius: 30px; + width: 60%; + padding: 25px; + margin-top: 50px; +} + +.headingText { + color: #ffffff; +} + +.links { + margin-top: 200px; +} + +.links .row { + width: 40%; +} + +.links .buttons { + background-color: rgba(0, 0, 0, 0.55); + padding: 20px; + border-radius: 12px; +} + +.links a { + text-decoration: none; + color: #ffffff; + font-weight: 700; + letter-spacing: 1px; + font-size: 25px; +} + +.links a:hover { + color: #000000; +} + +.links .buttons:hover { + background-color: rgba(52, 152, 219, 1); +} + +/* PNG Extraction Styles */ + .backdrop { /* position: absolute; */ background-image: url("../images/img1.png"); + height: 100%; + background-position: center; background-repeat: no-repeat; background-size: cover; - height: 95vh; } .row { diff --git a/modules/frontend/static/images/home.png b/modules/frontend/static/images/home.png new file mode 100644 index 0000000000000000000000000000000000000000..de214c8a339f81412722d9dade81976447590216 GIT binary patch literal 209297 zcmb4qXH-+)6YT{NK~U)(1f*B#y$VQ2dN0y@?@d5JdM^?Py+{iJ(wibZKxiS-e}E7| z6%te;;>-Vic;Da2TKSM!xyiY6&z_k*d;Wd<_a8u}t)Zm>;NbxP9_|JFy94B27^$n6 z7#PX2x;YDa`n$NXhKdM1XXObA^6+(I)zW^+s{Km+C2OdpD6ar(K*(!vkKhPa1y*T^ zhyON!7r^~{1Ox>4?&IF>-@i}zfRyL~ZapC(Atoh%LP0_Pgq)m`hKZJvih-J(oQ{o- zftmT~)2EcQ>>TVY984@vS^jqsJlt0w5I%TJMD&=2ikyn&|9kx#1;~jAq#wlH!(#{V z$?@)yp-y^{NOCp?={(lzS`hPauE%~4I-#S2g4-dd6yGMrm zWMK)*La!~v&7;}z>Z4QOn8SBGPPXTT)kJ%RWVy~xJUNNG&dRl*XY}f1K#3#a^Vh^` zX`YoW2^tcRDL2syeJ2;rJLaAZ8UjdK(B2|L zokpD!4HgFN$)awOkBWKn27H2z9%_0Xob_1AC*!KQ9kyBNkW5bZk>i*kUS!N9hm;~m zU6Ue*>p2}Ofppxswq}~UwxLBv;&Vy@5WyGR&09z&!ULY;iJIV(Du31`5U=)7=m!;e zzQ`-kpQ-N*C;%w^ol_Fy32Y3BfK2J6+@xIA+7yj|4~^8+?3#I6uHeUg5{#J0t@jB1ZtPi7rhPDkw2EUv%8{ ztyb(e{%bVI|v}+q@6+!FEzsO;$-Y52KtMfo0F?`cNM8GM3U^(iJb|<_ zHGo%RW@S%Bo2MX`n$yup&B6kZqR|4zCSgZGh7|m!(VTP4M%#_H-kf+L8l|6Xh@;V>BLQ} z+ZM8|zy&)(+(z8fkTs>JO$XO#ZWO*&3|l-1%zfvHm2hh)|6y->lNI^us~osX-T+@|w5e*_GzMO!5jxEs=Rm#Ew(BP zfINUPogQY1_mzM*m%4dc`nbG4^#>hoyyc5jP~=uav`!;i=T2M9*6KOC zBkq)j4)r2L{U_DcF^HE7KVk?4XIyQWKOtdyEH4(iZaT|kuFDGQ>tbl2)$8Iq48FCk z56vm~l32Hrz6~LYo5I$U>XHH{Qu)ULa)E7aQx_Lv{R2VGJ_a;_#)YJ;4HRU< zpoT)kbW3#G92Ukap=5e6WY)fvl@@u~W2VR7tnYQO*7kVT@AWfAScP3wnSVFqtfi== z#3O>v_}63K|5zYW6`s=82R2hHyHgTn;^&cbXo#s%=U7l^;vl$5RL5eGyb>Sp9wC75 zK#=%cTlxJsef2$pG4U1Rcl|yyq;aqDs^�^4JsJ(NhHENq11WFc^APi%>YkvDda3 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Ú$i=Jjpm9;@sJik* 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 literal 0 HcmV?d00001 diff --git a/modules/frontend/templates/Home.html b/modules/frontend/templates/Home.html new file mode 100644 index 0000000..c8bdc96 --- /dev/null +++ b/modules/frontend/templates/Home.html @@ -0,0 +1,168 @@ + + + + + + + PNG Extraction + + + + + + + + +
+
+
+
+

NIFFLER

+
+

+ A DICOM Framework for Machine Learning Pipelines and + Processing Workflows +

+
+
+ + +
+
+ + diff --git a/modules/frontend/templates/pngHome.html b/modules/frontend/templates/pngHome.html index ba8572f..b4a1738 100644 --- a/modules/frontend/templates/pngHome.html +++ b/modules/frontend/templates/pngHome.html @@ -48,7 +48,7 @@ > Meta Extraction -
@@ -343,23 +336,47 @@

-
+
-
- +
+
+

LOGS

+
+
+ {% if logs %} +

+ Errors: {{ logs[0] }} +

+

+ Status: {{ logs[1] }} +

+

+ Please check your output folder for + extracted Images !! +

+ {% else %} +

+ Run PNG Extraction to check Logs +

+ {% endif %}
+
diff --git a/modules/png-extraction/ImageExtractor.py b/modules/png-extraction/ImageExtractor.py index 643b9a7..ce258ad 100644 --- a/modules/png-extraction/ImageExtractor.py +++ b/modules/png-extraction/ImageExtractor.py @@ -21,62 +21,76 @@ from pydicom import datadict from pydicom import values -with open('config.json', 'r') as f: - niffler = json.load(f) +import pathlib +configs = {} -#Get variables for StoreScp from config.json. -print_images = niffler['PrintImages'] -print_only_common_headers = niffler['CommonHeadersOnly'] -dicom_home = niffler['DICOMHome'] #the folder containing your dicom files -output_directory = niffler['OutputDirectory'] -depth = niffler['Depth'] -processes = niffler['UseProcesses'] #how many processes to use. -flattened_to_level = niffler['FlattenedToLevel'] -email = niffler['YourEmail'] -send_email = niffler['SendEmail'] -no_splits = niffler['SplitIntoChunks'] -is16Bit = niffler['is16Bit'] +def initialize_Values(config_values): + global configs + configs = config_values + # Applying checks for paths + + p1 = pathlib.PureWindowsPath(configs['dcmFolder']) + dicom_home = p1.as_posix() #the folder containing your dicom files + + p2 = pathlib.PureWindowsPath(configs['outputFolder']) + output_directory = p2.as_posix() + + print_images = configs['printImages'] + print_only_common_headers = configs['headers'] + depth = int(configs['depth']) + processes = int(configs['useProcess']) #how many processes to use. + flattened_to_level = configs['level'] + email = configs['email'] + send_email = configs['sendEmail'] + no_splits = int(configs['chunks']) + is16Bit = configs['16Bit'] + + metadata_col_freq_threshold = 0.1 -metadata_col_freq_threshold = 0.1 + png_destination = output_directory + '/extracted-images/' + failed = output_directory +'/failed-dicom/' + maps_directory = output_directory + '/maps/' + meta_directory = output_directory + '/meta/' -png_destination = output_directory + '/extracted-images/' -failed = output_directory +'/failed-dicom/' -maps_directory = output_directory + '/maps/' -meta_directory = output_directory + '/meta/' + LOG_FILENAME = output_directory + '/ImageExtractor.out' + pickle_file = output_directory + '/ImageExtractor.pickle' -LOG_FILENAME = output_directory + '/ImageExtractor.out' -pickle_file = output_directory + '/ImageExtractor.pickle' -# record the start time -t_start = time.time() + # record the start time + t_start = time.time() -if not os.path.exists(output_directory): - os.makedirs(output_directory) + if not os.path.exists(output_directory): + os.makedirs(output_directory) -logging.basicConfig(filename=LOG_FILENAME, level=logging.DEBUG) + logging.basicConfig(filename=LOG_FILENAME, level=logging.DEBUG) -if not os.path.exists(maps_directory): - os.makedirs(maps_directory) + if not os.path.exists(maps_directory): + os.makedirs(maps_directory) -if not os.path.exists(meta_directory): - os.makedirs(meta_directory) + if not os.path.exists(meta_directory): + os.makedirs(meta_directory) -if not os.path.exists(png_destination): - os.makedirs(png_destination) + if not os.path.exists(png_destination): + os.makedirs(png_destination) -if not os.path.exists(failed): - os.makedirs(failed) + if not os.path.exists(failed): + os.makedirs(failed) -if not os.path.exists(failed + "/1"): - os.makedirs(failed + "/1") + if not os.path.exists(failed + "/1"): + os.makedirs(failed + "/1") -if not os.path.exists(failed + "/2"): - os.makedirs(failed + "/2") + if not os.path.exists(failed + "/2"): + os.makedirs(failed + "/2") -if not os.path.exists(failed + "/3"): - os.makedirs(failed + "/3") + if not os.path.exists(failed + "/3"): + os.makedirs(failed + "/3") -if not os.path.exists(failed + "/4"): - os.makedirs(failed + "/4") + if not os.path.exists(failed + "/4"): + os.makedirs(failed + "/4") + + logging.info("------- Values Initialization DONE -------") + final_res = Execute_Main_PNG_Extractor(pickle_file, dicom_home, output_directory, print_images, print_only_common_headers, depth, processes, flattened_to_level, email, send_email, no_splits, is16Bit, png_destination, + failed, maps_directory, meta_directory, LOG_FILENAME, metadata_col_freq_threshold, t_start) + return final_res #%%Function for getting tuple for field,val pairs def get_tuples(plan, outlist = None, key = ""): @@ -128,7 +142,8 @@ def extract_headers(f_list_elem): # dicom images should not have more than 300 if len(kv)>500: logging.debug(str(len(kv)) + " dicoms produced by " + ff) - kv.append(('file',chunk[nn])) #adds my custom field with the original filepath + # kv.append(('file',chunk[nn])) #adds my custom field with the original filepath + kv.append(('file', f_list_elem[1])) kv.append(('has_pix_array',c)) #adds my custom field with if file has image if c: kv.append(('category','uncategorized')) #adds my custom category field - useful if classifying images before processing @@ -142,7 +157,7 @@ def extract_headers(f_list_elem): # filemapping: dicom to png paths (as str) # fail_path: dicom to failed folder (as tuple) # found_err: error code produced when processing -def extract_images(i): +def extract_images(filedata, i, png_destination, flattened_to_level, failed, is16Bit): ds = dicom.dcmread(filedata.iloc[i].loc['file'], force=True) #read file in found_err=None filemapping = "" @@ -183,7 +198,7 @@ def extract_images(i): pngfile = png_destination+folderName+'/' + hashlib.sha224(imName.encode('utf-8')).hexdigest() + '.png' dicom_path = filedata.iloc[i].loc['file'] image_path = png_destination+folderName+'/' + hashlib.sha224(imName.encode('utf-8')).hexdigest() + '.png' - if is16Bit: + if (is16Bit == 'True' or is16Bit == 'true'): # write the PNG file as a 16-bit greyscale image_2d = ds.pixel_array.astype(np.double) # # Rescaling grey scale between 0-255 @@ -245,7 +260,7 @@ def fix_mismatch_callback(raw_elem, **kwargs): return raw_elem -def get_path(depth): +def get_path(depth, dicom_home): directory = dicom_home + '/' i = 0 while i < depth: @@ -273,81 +288,87 @@ def fix_mismatch(with_VRs=['PN', 'DS', 'IS']): 'with_VRs': with_VRs, } -fix_mismatch() -if processes == 0.5: # use half the cores to avoid high ram usage - core_count = int(os.cpu_count()/2) -elif processes == 0: # use all the cores - core_count = int(os.cpu_count()) -elif processes < os.cpu_count(): # use the specified number of cores to avoid high ram usage - core_count = processes -else: - core_count = int(os.cpu_count()) -#%% get set up to create dataframe -dirs = os.listdir(dicom_home) -#gets all dicom files. if editing this code, get filelist into the format of a list of strings, -#with each string as the file path to a different dicom file. -file_path = get_path(depth) - -if os.path.isfile(pickle_file): - f=open(pickle_file,'rb') - filelist=pickle.load(f) -else: - filelist=glob.glob(file_path, recursive=True) #this searches the folders at the depth we request and finds all dicoms - pickle.dump(filelist,open(pickle_file,'wb')) -file_chunks = np.array_split(filelist,no_splits) -logging.info('Number of dicom files: ' + str(len(filelist))) - -try: - ff = filelist[0] #load first file as a template to look at all -except IndexError: - logging.error("There is no file present in the given folder in " + file_path) - sys.exit(1) - -plan = dicom.dcmread(ff, force=True) -logging.debug('Loaded the first file successfully') - -keys = [(aa) for aa in plan.dir() if (hasattr(plan, aa) and aa!='PixelData')] -#%%checks for images in fields and prints where they are -for field in plan.dir(): - if (hasattr(plan, field) and field!='PixelData'): - entry = getattr(plan, field) - if type(entry) is bytes: - logging.debug(field) - logging.debug(str(entry)) -for i,chunk in enumerate(file_chunks): - csv_destination = "{}/meta/metadata_{}.csv".format(output_directory,i) - mappings ="{}/maps/mapping_{}.csv".format(output_directory,i) - fm = open(mappings, "w+") - filemapping = 'Original DICOM file location, PNG location \n' - fm.write(filemapping) - # add a check to see if the metadata has already been extracted - #%%step through whole file list, read in file, append fields to future dataframe of all files - headerlist = [] - #start up a multi processing pool - #for every item in filelist send data to a subprocess and run extract_headers func - #output is then added to headerlist as they are completed (no ordering is done) - with Pool(core_count) as p: - res= p.imap_unordered(extract_headers,enumerate(chunk)) - for i,e in enumerate(res): - headerlist.append(e) - data = pd.DataFrame(headerlist) - logging.info('Chunk ' + str(i) + ' Number of fields per file : ' + str(len(data.columns))) - #%%find common fields - #make dataframe containing all fields and all files minus those removed in previous block - #%%export csv file of final dataframe - export_csv = data.to_csv (csv_destination, index = None, header=True) - fields=data.keys() - count = 0 #potential painpoint - #writting of log handled by main process - if print_images: - logging.info("Start processing Images") - filedata=data - total = len(chunk) - stamp = time.time() + +def Execute_Main_PNG_Extractor(pickle_file, dicom_home, output_directory, print_images, print_only_common_headers, depth, processes, flattened_to_level, email, send_email, no_splits, is16Bit, png_destination, + failed, maps_directory, meta_directory, LOG_FILENAME, metadata_col_freq_threshold, t_start): + fix_mismatch() + if processes == 0.5: # use half the cores to avoid high ram usage + core_count = int(os.cpu_count()/2) + elif processes == 0: # use all the cores + core_count = int(os.cpu_count()) + elif processes < os.cpu_count(): # use the specified number of cores to avoid high ram usage + core_count = processes + else: + core_count = int(os.cpu_count()) + #%% get set up to create dataframe + dirs = os.listdir(dicom_home) + #gets all dicom files. if editing this code, get filelist into the format of a list of strings, + #with each string as the file path to a different dicom file. + file_path = get_path(depth, dicom_home) + + if os.path.isfile(pickle_file): + f=open(pickle_file,'rb') + filelist=pickle.load(f) + else: + filelist=glob.glob(file_path, recursive=True) #this searches the folders at the depth we request and finds all dicoms + pickle.dump(filelist,open(pickle_file,'wb')) + file_chunks = np.array_split(filelist,no_splits) + logging.info('Number of dicom files: ' + str(len(filelist))) + + try: + ff = filelist[0] #load first file as a template to look at all + except IndexError: + logging.error("There is no file present in the given folder in " + file_path) + sys.exit(1) + + plan = dicom.dcmread(ff, force=True) + logging.debug('Loaded the first file successfully') + + keys = [(aa) for aa in plan.dir() if (hasattr(plan, aa) and aa!='PixelData')] + #%%checks for images in fields and prints where they are + for field in plan.dir(): + if (hasattr(plan, field) and field!='PixelData'): + entry = getattr(plan, field) + if type(entry) is bytes: + logging.debug(field) + logging.debug(str(entry)) + + for i,chunk in enumerate(file_chunks): + csv_destination = "{}/meta/metadata_{}.csv".format(output_directory,i) + mappings ="{}/maps/mapping_{}.csv".format(output_directory,i) + fm = open(mappings, "w+") + filemapping = 'Original DICOM file location, PNG location \n' + fm.write(filemapping) + # add a check to see if the metadata has already been extracted + #%%step through whole file list, read in file, append fields to future dataframe of all files + headerlist = [] + #start up a multi processing pool + #for every item in filelist send data to a subprocess and run extract_headers func + #output is then added to headerlist as they are completed (no ordering is done) with Pool(core_count) as p: - res = p.imap_unordered(extract_images,range(len(filedata))) - for out in res: - (fmap,fail_path,err) = out + res= p.imap_unordered(extract_headers, enumerate(chunk)) + for i,e in enumerate(res): + headerlist.append(e) + data = pd.DataFrame(headerlist) + logging.info('Chunk ' + str(i) + ' Number of fields per file : ' + str(len(data.columns))) + #%%find common fields + #make dataframe containing all fields and all files minus those removed in previous block + #%%export csv file of final dataframe + export_csv = data.to_csv(csv_destination, index = None, header=True) + fields=data.keys() + count = 0 #potential painpoint + #writting of log handled by main process + if print_images: + logging.info("Start processing Images") + filedata = data + total = len(chunk) + stamp = time.time() + for i in range(len(filedata)): + res = extract_images(filedata, i, png_destination, flattened_to_level, failed, is16Bit) + # (fmap,fail_path,err) = out + fmap = res[1] + fail_path = res[0] + err = res[-1] if err: count +=1 copyfile(fail_path[0],fail_path[1]) @@ -355,60 +376,138 @@ def fix_mismatch(with_VRs=['PN', 'DS', 'IS']): logging.error(err_msg) else: fm.write(fmap) - fm.close() - logging.info('Chunk run time: %s %s', time.time() - t_start, ' seconds!') - - -logging.info('Generating final metadata file') - -#identify the -col_names = dict() -all_headers = dict() - -metas = glob.glob( "{}*.csv".format(meta_directory)) -#for each meta file identify the columns that are not na's for at least 10% (metadata_col_freq_threshold) of data -for meta in metas: - m = pd.read_csv(meta,dtype='str') - d_len = m.shape[0] - for e in m.columns: - if np.sum(m[e].isna()) < (1-metadata_col_freq_threshold)*d_len: # Column e is populated in at least 10% of rows (i.e. isNaN in <90% of rows) - if e in col_names: - col_names[e] += 1 + ''' + with Pool(core_count) as p: + res = p.imap_unordered(extract_images,range(len(filedata))) + for out in res: + (fmap,fail_path,err) = out + if err: + count +=1 + copyfile(fail_path[0],fail_path[1]) + err_msg = str(count) + ' out of ' + str(len(chunk)) + ' dicom images have failed extraction' + logging.error(err_msg) + else: + fm.write(fmap) + ''' + fm.close() + logging.info('Chunk run time: %s %s', time.time() - t_start, ' seconds!') + + + logging.info('Generating final metadata file') + + #identify the + col_names = dict() + all_headers = dict() + + metas = glob.glob( "{}*.csv".format(meta_directory)) + #for each meta file identify the columns that are not na's for at least 10% (metadata_col_freq_threshold) of data + for meta in metas: + m = pd.read_csv(meta,dtype='str') + d_len = m.shape[0] + for e in m.columns: + if np.sum(m[e].isna()) < (1-metadata_col_freq_threshold)*d_len: # Column e is populated in at least 10% of rows (i.e. isNaN in <90% of rows) + if e in col_names: + col_names[e] += 1 + else: + col_names[e] = 1 + # all_headers keeps track of number of appearances of each header. We later use this count to ensure that the headers we use are present in all metadata files. + if e in all_headers: + all_headers[e] += 1 else: - col_names[e] = 1 - # all_headers keeps track of number of appearances of each header. We later use this count to ensure that the headers we use are present in all metadata files. - if e in all_headers: - all_headers[e] += 1 - else: - all_headers[e] = 1 - -loadable_names = list() -for k in col_names.keys(): - if k in all_headers and all_headers[k] >= no_splits: # no_splits == number of batches used - loadable_names.append(k) # use header only if it's present in every metadata file - -#load every metadata file using only valid columns -meta_list = list() -for meta in metas: - m = pd.read_csv(meta,dtype='str',usecols=loadable_names) - meta_list.append(m) -merged_meta = pd.concat(meta_list,ignore_index=True) -merged_meta.to_csv('{}/metadata.csv'.format(output_directory),index=False) -#getting a single mapping file -logging.info('Generatign final mapping file') -mappings = glob.glob("{}/maps/*.csv".format(output_directory)) -map_list = list() -for mapping in mappings: - map_list.append(pd.read_csv(mapping,dtype='str')) -merged_maps = pd.concat(map_list,ignore_index=True) -if print_only_common_headers: - mask_common_fields = merged_maps.isnull().mean() < 0.1 - common_fields = set(np.asarray(merged_maps.columns)[mask_common_fields]) - merged_maps = merged_maps[common_fields] -merged_maps.to_csv('{}/mapping.csv'.format(output_directory),index=False) - - -if send_email: - subprocess.call('echo "Niffler has successfully completed the png conversion" | mail -s "The image conversion has been complete" {0}'.format(email), shell=True) -# Record the total run-time -logging.info('Total run time: %s %s', time.time() - t_start, ' seconds!') + all_headers[e] = 1 + + loadable_names = list() + for k in col_names.keys(): + if k in all_headers and all_headers[k] >= no_splits: # no_splits == number of batches used + loadable_names.append(k) # use header only if it's present in every metadata file + + #load every metadata file using only valid columns + meta_list = list() + for meta in metas: + m = pd.read_csv(meta,dtype='str',usecols=loadable_names) + meta_list.append(m) + merged_meta = pd.concat(meta_list,ignore_index=True) + merged_meta.to_csv('{}/metadata.csv'.format(output_directory),index=False) + #getting a single mapping file + logging.info('Generatign final mapping file') + mappings = glob.glob("{}/maps/*.csv".format(output_directory)) + map_list = list() + for mapping in mappings: + map_list.append(pd.read_csv(mapping,dtype='str')) + merged_maps = pd.concat(map_list,ignore_index=True) + if (print_only_common_headers == 'True' or print_only_common_headers == 'true'): + mask_common_fields = merged_maps.isnull().mean() < 0.1 + common_fields = set(np.asarray(merged_maps.columns)[mask_common_fields]) + merged_maps = merged_maps[common_fields] + merged_maps.to_csv('{}/mapping.csv'.format(output_directory),index=False) + + if (send_email == 'True' or send_email == 'true'): + subprocess.call('echo "Niffler has successfully completed the png conversion" | mail -s "The image conversion has been complete" {0}'.format(email), shell=True) + # Record the total run-time + logging.info('Total run time: %s %s', time.time() - t_start, ' seconds!') + logs = [] + logs.append(err) + logs.append("DONE") + return logs + + +if __name__ == "__main__": + with open('config.json', 'r') as f: + niffler = json.load(f) + + #Get variables for StoreScp from config.json. + print_images = niffler['PrintImages'] + print_only_common_headers = niffler['CommonHeadersOnly'] + dicom_home = niffler['DICOMHome'] #the folder containing your dicom files + output_directory = niffler['OutputDirectory'] + depth = niffler['Depth'] + processes = niffler['UseProcesses'] #how many processes to use. + flattened_to_level = niffler['FlattenedToLevel'] + email = niffler['YourEmail'] + send_email = niffler['SendEmail'] + no_splits = niffler['SplitIntoChunks'] + is16Bit = niffler['is16Bit'] + + metadata_col_freq_threshold = 0.1 + + png_destination = output_directory + '/extracted-images/' + failed = output_directory +'/failed-dicom/' + maps_directory = output_directory + '/maps/' + meta_directory = output_directory + '/meta/' + + LOG_FILENAME = output_directory + '/ImageExtractor.out' + pickle_file = output_directory + '/ImageExtractor.pickle' + # record the start time + t_start = time.time() + + if not os.path.exists(output_directory): + os.makedirs(output_directory) + + logging.basicConfig(filename=LOG_FILENAME, level=logging.DEBUG) + + if not os.path.exists(maps_directory): + os.makedirs(maps_directory) + + if not os.path.exists(meta_directory): + os.makedirs(meta_directory) + + if not os.path.exists(png_destination): + os.makedirs(png_destination) + + if not os.path.exists(failed): + os.makedirs(failed) + + if not os.path.exists(failed + "/1"): + os.makedirs(failed + "/1") + + if not os.path.exists(failed + "/2"): + os.makedirs(failed + "/2") + + if not os.path.exists(failed + "/3"): + os.makedirs(failed + "/3") + + if not os.path.exists(failed + "/4"): + os.makedirs(failed + "/4") + logging.info("---------------- From the command line ----------------") + Execute_Main_PNG_Extractor(pickle_file, dicom_home, output_directory, print_images, print_only_common_headers, depth, processes, flattened_to_level, email, send_email, no_splits, is16Bit, png_destination, + failed, maps_directory, meta_directory, LOG_FILENAME, metadata_col_freq_threshold, t_start) \ No newline at end of file diff --git a/modules/png-extraction/__pycache__/ImageExtractor.cpython-38.pyc b/modules/png-extraction/__pycache__/ImageExtractor.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c71d3e45928685556a0d3874af29b7a51e33b86e GIT binary patch literal 11493 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 RH#`LQe#1j9%l+T+{2w6|ym0^k literal 0 HcmV?d00001 diff --git a/modules/png-extraction/config.json b/modules/png-extraction/config.json index 3939e71..b431f70 100644 --- a/modules/png-extraction/config.json +++ b/modules/png-extraction/config.json @@ -1,13 +1,13 @@ { - "DICOMHome": "/Users/pradeeban/Downloads", - "OutputDirectory": "/Users/pradeeban/Downloads/root", - "Depth": 0, - "SplitIntoChunks": 1, - "PrintImages": true, - "CommonHeadersOnly": false, - "UseProcesses": 0, - "FlattenedToLevel": "patient", - "is16Bit":true, - "SendEmail": true, - "YourEmail": "test@test.test" + "DICOMHome": "E:/MyProjects/GSOC/niffler-ui/lol/imgs", + "OutputDirectory": "E:/MyProjects/GSOC/niffler-ui/lol/outputs", + "Depth": 1, + "SplitIntoChunks": 1, + "PrintImages": true, + "CommonHeadersOnly": false, + "UseProcesses": 0, + "FlattenedToLevel": "patient", + "is16Bit": true, + "SendEmail": false, + "YourEmail": "test@test.test" } From dc675a00ad4bd3a83d7531a8766a2764d1daba6c Mon Sep 17 00:00:00 2001 From: Pradeeban Kathiravelu Date: Wed, 9 Jun 2021 09:48:23 -0400 Subject: [PATCH 11/46] Update .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c9d8c6c..3c9dc5b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ target/ .idea/ **/*.iml **/*.csv +__pycache__ +**/*.pyc From d8e79b9d786a41a0122a884644cde5269b443a65 Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Wed, 9 Jun 2021 20:18:53 +0530 Subject: [PATCH 12/46] PNG-Extraction changes made --- modules/png-extraction/ImageExtractor.py | 24 +++++------------------- modules/png-extraction/config.json | 8 ++++---- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/modules/png-extraction/ImageExtractor.py b/modules/png-extraction/ImageExtractor.py index ce258ad..d0c152b 100644 --- a/modules/png-extraction/ImageExtractor.py +++ b/modules/png-extraction/ImageExtractor.py @@ -88,7 +88,7 @@ def initialize_Values(config_values): os.makedirs(failed + "/4") logging.info("------- Values Initialization DONE -------") - final_res = Execute_Main_PNG_Extractor(pickle_file, dicom_home, output_directory, print_images, print_only_common_headers, depth, processes, flattened_to_level, email, send_email, no_splits, is16Bit, png_destination, + final_res = Execute(pickle_file, dicom_home, output_directory, print_images, print_only_common_headers, depth, processes, flattened_to_level, email, send_email, no_splits, is16Bit, png_destination, failed, maps_directory, meta_directory, LOG_FILENAME, metadata_col_freq_threshold, t_start) return final_res @@ -142,8 +142,7 @@ def extract_headers(f_list_elem): # dicom images should not have more than 300 if len(kv)>500: logging.debug(str(len(kv)) + " dicoms produced by " + ff) - # kv.append(('file',chunk[nn])) #adds my custom field with the original filepath - kv.append(('file', f_list_elem[1])) + kv.append(('file', f_list_elem[1])) #adds my custom field with the original filepath kv.append(('has_pix_array',c)) #adds my custom field with if file has image if c: kv.append(('category','uncategorized')) #adds my custom category field - useful if classifying images before processing @@ -289,7 +288,7 @@ def fix_mismatch(with_VRs=['PN', 'DS', 'IS']): } -def Execute_Main_PNG_Extractor(pickle_file, dicom_home, output_directory, print_images, print_only_common_headers, depth, processes, flattened_to_level, email, send_email, no_splits, is16Bit, png_destination, +def Execute(pickle_file, dicom_home, output_directory, print_images, print_only_common_headers, depth, processes, flattened_to_level, email, send_email, no_splits, is16Bit, png_destination, failed, maps_directory, meta_directory, LOG_FILENAME, metadata_col_freq_threshold, t_start): fix_mismatch() if processes == 0.5: # use half the cores to avoid high ram usage @@ -376,19 +375,6 @@ def Execute_Main_PNG_Extractor(pickle_file, dicom_home, output_directory, print_ logging.error(err_msg) else: fm.write(fmap) - ''' - with Pool(core_count) as p: - res = p.imap_unordered(extract_images,range(len(filedata))) - for out in res: - (fmap,fail_path,err) = out - if err: - count +=1 - copyfile(fail_path[0],fail_path[1]) - err_msg = str(count) + ' out of ' + str(len(chunk)) + ' dicom images have failed extraction' - logging.error(err_msg) - else: - fm.write(fmap) - ''' fm.close() logging.info('Chunk run time: %s %s', time.time() - t_start, ' seconds!') @@ -447,7 +433,7 @@ def Execute_Main_PNG_Extractor(pickle_file, dicom_home, output_directory, print_ logging.info('Total run time: %s %s', time.time() - t_start, ' seconds!') logs = [] logs.append(err) - logs.append("DONE") + logs.append("The PNG conversion is SUCCESSFUL") return logs @@ -509,5 +495,5 @@ def Execute_Main_PNG_Extractor(pickle_file, dicom_home, output_directory, print_ if not os.path.exists(failed + "/4"): os.makedirs(failed + "/4") logging.info("---------------- From the command line ----------------") - Execute_Main_PNG_Extractor(pickle_file, dicom_home, output_directory, print_images, print_only_common_headers, depth, processes, flattened_to_level, email, send_email, no_splits, is16Bit, png_destination, + Execute(pickle_file, dicom_home, output_directory, print_images, print_only_common_headers, depth, processes, flattened_to_level, email, send_email, no_splits, is16Bit, png_destination, failed, maps_directory, meta_directory, LOG_FILENAME, metadata_col_freq_threshold, t_start) \ No newline at end of file diff --git a/modules/png-extraction/config.json b/modules/png-extraction/config.json index b431f70..5e4f2d8 100644 --- a/modules/png-extraction/config.json +++ b/modules/png-extraction/config.json @@ -1,13 +1,13 @@ { - "DICOMHome": "E:/MyProjects/GSOC/niffler-ui/lol/imgs", - "OutputDirectory": "E:/MyProjects/GSOC/niffler-ui/lol/outputs", - "Depth": 1, + "DICOMHome": "/Users/pradeeban/Downloads", + "OutputDirectory": "/Users/pradeeban/Downloads/root", + "Depth": 0, "SplitIntoChunks": 1, "PrintImages": true, "CommonHeadersOnly": false, "UseProcesses": 0, "FlattenedToLevel": "patient", "is16Bit": true, - "SendEmail": false, + "SendEmail": true, "YourEmail": "test@test.test" } From ba6b6004a6a6c08c63d2625f192e1aa9d994f3a7 Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Wed, 9 Jun 2021 20:41:44 +0530 Subject: [PATCH 13/46] Changes --- modules/frontend/server.py | 2 +- .../__pycache__/ImageExtractor.cpython-38.pyc | Bin 11493 -> 0 bytes modules/png-extraction/config.json | 24 +++++++++--------- 3 files changed, 13 insertions(+), 13 deletions(-) delete mode 100644 modules/png-extraction/__pycache__/ImageExtractor.cpython-38.pyc diff --git a/modules/frontend/server.py b/modules/frontend/server.py index ff865ea..65b8ec5 100644 --- a/modules/frontend/server.py +++ b/modules/frontend/server.py @@ -15,7 +15,7 @@ def PNG_Extraction(): config_values = {} @app.route('/', methods=['POST']) -def getPNGValues(): +def extract_png(): if request.method =='POST': config_values["dcmFolder"] = request.form['DICOMFolder'] config_values["outputFolder"] = request.form['outputFolder'] diff --git a/modules/png-extraction/__pycache__/ImageExtractor.cpython-38.pyc b/modules/png-extraction/__pycache__/ImageExtractor.cpython-38.pyc deleted file mode 100644 index c71d3e45928685556a0d3874af29b7a51e33b86e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11493 zcmbtaTWlNId7c~Z6h%?g)wi)MTb3+KwtV+`z4or`UA11VV|g!`#APU+p)?dZq|Xd( zi+h*`(Y8&4x^dG*+XO*M6+zKWfj$&{X@LSkk%yw_L!XLb(1)N6P%KcOFL?+SLEY~^ zLrSu;xfG!^=RfoR=YIb4pZ_v%_w^+e{I2}N_rCDMcNFD6>1FRPkCz*GJVRF$rZBat zSRqs`74KSAtLc`mhB(7AWSNK+kuYjSC5%}y3FB5AP_HIxNh{f{ld@8hmbTIo_E~)r zW~_b*v(|uwIcreDyp;zus=3;bH6+`JAU|vk%X}315o@H|_NX=LD8(@rWAP6ZYn)wV zNtXIRwGOZ}>jUN>%dmdHLoCY%01vYq8w5PU@@$9=ujyr#jj&PFFxeOz2j!?e!47O_ z&vdTX$Jjx3XiNKA)|#{~*h=RbeRXLQuQY){)W_LP0nK4s6^r|mg=-accWwa?k- z?F;rrtjmIZWor;TrLHK`mp-QRu{^B?@lw69S$3*6^2XhIy$TpD)!nkQMpRbxi_9tc zgnd-3Hf^t@Qq}Hn1dvYGYk0i>1w|-D#aBC;kAB9mDs5G$9erDAYhA1+vAWPD)<8LZ zOld~|4Zx^Rn8=v2rM6=-AC>tyurbgRfN{ViU;;2D%Oqu)G;k^4`dGT15eDlUQQG~0 z8Ne)HKi~jh7BB}m05}Mk1I+u8P6VSI@}relCoYU_)%#aJ+DTAORs3WnB?ghE*&uON zx6}Yi{X~{RZU>tKFQ{H#;*sCM_e=bR3h&r1BF#3R3pXL-5rs>ecF_`Zie)TMvW zrOWp{^x-c3J6*bb-$Nhi(!bxO%XiR|ojx`yN0JeFp$S7oL{!8?TqHzNq(oZuiGGn4 z10p8|MMey54Yr5HF!*Y0IY_Wl?RLG&Y#yZRO~29fyKkumcU*s|R$Q~aAhvE7QN#-( z%x?JWL9*%DcX_>Jdq7p~M|L%c*tMcl4HBO1vRg76Ev+}*4HP(g@m0tFuWx>Xet-MQ zC%Jk1iO-8A-)5&B+5Y?|nfWqm0|D->*XBQo&ew_!PeR)-&VMpEFGq4KtXSuB80#m) z`;iT&v{AJ`(tgc^_E)6K(!1o&`MT@SJbWoDP5HJMxT}ORnQP#j4XP`cB<7 z7vEaGWp;~9_Xn}?d|?$DMSs2OtXlndo7{faaasMbcNaJ-Ui1o$y5~FzbSzq6_)g6Z zw7M4&NUatQAYx>>-Y*XnMeyUjd>RSOc0aEGz~xpKAW`?hPdf?qGl4d)pulcen~ zbhqBE7raK*!9-(@CpSGfP{a0-R6-BcLYdnS3;sH{y>+l$(BE*^3e5I=$CV2oL_;PB zGPL=7#+{*7_PnR>y!CqF_R^hO%QxS=6{Ny#FVLO`F~8vXMebXPGFq(`(AP%_zfN3m zY;JBIJ)Z7zA9nil`|3_YX9HG1M|TutMTAm!qkA#r#k zsK>Rmn$)r~%^=r%a%x7~`y|zas;2DxJvTId13d*w3A)eDkG6vzDYyR&a@$vgwxu$4 z!{9jaOoL3+gz6n!Qy{^Wj=HTZD$mphkJ@@G<7+|@`WEEiyNWw&D89ati?^Z$!j~F{U;2>t+eJgVAsYCH#zl&L$Ep z`c$X8LnJZR*oJ}DGq|7nMZ(V#u8h*E^6)Q!8xYAb|8R0=M7heK9MNSzPvdDv+HVwDBF&zS^rN$D;{-L=lnhcqRqi+3{sgS8t#eTEq7@ z8@73i^Ex+ew^VPszRhhGB@6e$Yuh)=`c%wdueex1!-64Jz$rtc=SCJ_4G#aX*W>gJN7AGsGCUG8fYF?eg zxyz_|jZR-u;zJk*O}6ubo`i*0qIl$a`*UQ30zOuu6)6=J{HrqUsn*t++BRBAp;xqa z1ZOL&D)h(nw$VL5(3|8LF+>y`Y~ZvqXf8N2zQh0*0pH;C67Py=C(dxbNOKA~Nn)Vg zbl(jAp~NJpw40Q9$@PAM+6T|2mhXS0Vn6DGXET9 z)KU;Bt=pxIK-+k-awfW zZEx>4jtrTka=U+Qka)#5D;0>;F{OfS9)r=zbZj|H%XW+z?KqyqGrfgPs~8YbkxH}^ zd$GngHW&327X2+K(d`sOXlz5{f3&9*sm5tP4z)GiNiuBfw%SQSt?fg5X()0~iP3M8 zRM=;#Yodh|%s`b7?`tE|%J0FX(Q5x5cI<2SciEA8%Ixillx^96|6VLElTR6e+u73x zjXcwu+=G$5?s@M=i7ZBu?;Z+_f@Ry;|L;h4MwtD@BkZnNZ%(NfYe)lPV9yA#t1v9W zRsMN(21SJBuI{~>csd)60V7FL7^Cf zLUB)w!4y2uIVfUMsJ(ECV7?s!$Nj?7f<6w9KnS)IU8Q8~i({mepvQaY@eq16c>0&@ z@yM^zqx&D|@38Do{F42dzfyl3{T-40{r4}~-_c*GKd5=qP>hvzTp$z6+=d{$&r3-8 zO-jm0KK{w`=g%!Ne;nI|Y7Lu*GE0lCNYgK$zD%wMY2p;>8y2qdCfl^KD+HEYTkEU#id&-pQ!WJz>3Ws+uK_tDT~~?hY~Gg4$699+~-ZGC_YlDPtg^m z{NVZj+z7PCs~>0af-5Vr#_dh7RIJ)oeAV%#jVcQ&Kd9fBR{0YYYiYA)pe9?|*?BaL zE32g~%(t|2c+SsHoAN5->nLa$PPJZw+Ep)c2k-nCC6h>?p^$ka^)RvngQ&M&Tv#{{ z(8dspx#G~(W2P#hxc`V_()Lw*&T@Ergq2f8KunqU@aqM#7-7krL>UAFbhYztrA|T z0`}h+?7Rn8+gd9tOT*IZmd$s|-V4iqR(UvsgRWEzvKF7JZRn{F%MWM5>sTl_`ucJZ zf9&|{g?H|GIM3QU_ogG9v?PmM4UcT@hrR~>A#i*NNf3v14UN8JPwQ5k7avOu!?GfL4S}bk+7;SmONE9qR>1k=X1sOGp1x>5`k3lJE zMDPBfp}}PC>T7ez&{B{I%%GGDT__t0KY@wrm&h!J=?bVnghoZ`Hl`X~<{oNEdD7Bm z=B12lMP^7U$RSS~(gVUzqZB-3J^u-RO11X!T%0T>V&xdrX^-}h6i`F@1)ywx1Mil0 zciGYwSNMB)d12d01K*hmErAbrY(*J-Ba*kUmKhKiXv!w;fJK!6S#kD8Q!AndVa2 zbnE`C$?enR?x{8Wo!aCVYkD~RvI!49CbNz<_)dX3_vR$+ZjnP6Ky-QXwd+R%*y)^D z%)Dxn+COW4?sHRjm#1dUsl^pamsX}8Jdh=6s5fOHvtGskO-NPr@0pl#Z*)$XCSrp3 zOp;|L84;McDGf@jmW%1^G`70C7MO8w6@IGnl!wm3sob;uCU;+$hiq-RjwK>e*b!Fl zK1T7=oAcPIR(mCFth&u*bsBP=JMjM%tNXUhbh;PjEJ~Pl*AB;7G;wm|HiZM@e+XV# z#sQ_X5nf$%D6An`1$hjy3Off3F{e*M7s7*z`;l7P&)4a_XBDQc;>A%)$;35ytDf_3 zB3~LLO!xOHAQ@34$-GAqV&yGJ84-n-N&}mKnJI?Av;!0^7d|07v-a-u-`51#qGY8o^ z2o+7`(NMagPM#%1&!0ck8D`Qh?;1NJl~FN<-cwJt-&Ig{jHO96{v8F8zCM=uApRAl zGfo~mtiX6L|KLvhfPdgCia4-ECf{{&fc0#}Co)Krzx*=v*5?9BJKPTpij+A0o#4!V62Hu;o?x@kLoW(dUp|coMUYw;d z!AC>uHz(IYd-(f!o8QKnkfS~jj#A`CVNh-Ba-TkwltWbBKeMH@7i4 zCf4q}IFDQZ!}bOLMCGiwz>Y8ze0@<|tSrb`Tte<7O1SM4;OukaqCiZybAB6MCYBTf zql&nc#}$bg{-Zb{7B-^DeNQZi6A=YQC}ylH*AzF7vcO$aQ0AO0^Fwj&Q_28$4Q)OA z5}x1iFJRP{#RBaUMsTuyMO=}!4X&Z~6|7-$BLd@16PLjuSLDhV4>eius+^a(J1=pW z9mT$16jxDJ#(bX{4=-ZYz+T2z8tP$cQoIYJzVSda2-bLNf`Zx_wUF}BNyRualLbCTj^YeJe%22 z+OPOmu+mq>^~%X@RlFkOn>WODaRa<`9oT7h>ctVlw@qzEyfes!Z7%vRRj#pFl#62~ z5q5fKCUg5a1!wW5xQR76LnEdfn-6nP70xV&mXGp-S3ZW$5NbJr##iq{t4kmf)a0H6 zb?y;pt&!zsZ55X|E?DU%#5DuY?^UyP1{WH12gid#AdyfNY{YteTu}1LT|*nL87ljP zlRA_%8(R~1>IfDxS*(m;mmfCZHA@sPEH|sw%@>p)8YS)vg>DmX0?8UQRqT)IE0A=M z@i>VpQxJau`~&Twfmy2jH53GRgMb!jb7%PHk^M-OuEX#$BPVf?yheHk+~AGtulH`T zD3q?G5?5MtKRd=YcMS$$vFcq%rq_V%v^Da6d!7PPGLbjy`|Y`s_oy{Y1PWVr?W95% zuRT81ntiKx;V&+|_ST!>l28w%#m$-s@0qa5Df!&t&tUqeUVy6OAT$wT#^CSk72(*Z^cc-7ih0rlilqO?Id%(_{cWH z*qgZ<9cvARqu@aTM5&%VG=O!XzTFnhzTNyKt0KLo7s!^$uYKK@3&`Y zX{6s5{1;(l55trE9c+yiLlhZdL6{p>+F5h!+%CxJwEDX%Op8m^Z)2mkQKNOJ*WVg= zPpVJdr4GC6>uo^TsgEkWNlA_>XF@7juyVW5`Fh=)Sa!UhKwe2a=k!*D3VIyi=b zu~x0G!r9)eHTai6wo=m19|mfrYo5;l9XjT3Q~l%;u~g_;AR`;`pG^d^;otFhog}L_+v9gBA4MbrwjTd&qLN~0Ww1_-LKAz9&k1ZMeeq#DKx;57 zv4{=7P38jy-K))-i!cxZ&R~JSKrG1PBp!kYESfc28qu7D2ciVkx@YqZ>LE^{2&y4{ z1{i#^29J*C7WrqWD19u0LkFLQ=Ry)$M$In5)v63a? z{&0g48R6u6!H!D9GEmEwbl1?vBVI!I@Y4!jr3U^634%T(9`F^0>+?-%s8Z|%Ld5FF zTD%Zt$5#l_jROzPa1VN0B9egp&UZ%#Ety- zWamEdGI{=rwT5Ms(E%sFOQ4hMD2Aj^^VX~ceFH#WWq9DiYPA=+u_7ciz+wafwPr;a zJ~42u3^6cStzn%&-LMi=M8=D(6rz`F_#S~U7EMW_1Gv}Zq$L|p0l7*n@QEggR%%~<$#T%{ZTc8GSo%}e|b={vK8Rr)gsRpVxC%F z`oG9a1s?^?63(0g1e#kYv{E=C_?~R8;=yUCaw~Or_e&sa;5E5wZ-n0iy@hW_aBAsB zdxJhZ>3>UpLbTZPm{^fT`d-LNzrFLl5H>%1yZ1H8{rVmHc*sh8w%+9Ot060~())nO z>R%yu>yqo&(Z__BFve$ zdA%kz72*a2n&EJO(Y?Rew6-k0;nO!lZ}4BDG4lR4eWxl#5Eq0oXvz3rQFnhw$rK^` zYZMdTy^=#gs5jWElxF-R6tW_S#y2)Y2_0=<97T`+H4$QsO{tlMqW1%8l`MTu3X^ot zag%aKD7it&3MCCnzD|jB5Relpyis~vO{cI>kQWC3d!l|9i4}9hyKs+qBNtdG@=}!h z`GQW$vrAO&)1;jgp(O3(rNxg^j@;z@Bqh_7$g61A!6tK3i;G{YA$(o6uhT8!eH#g# zj^6h-`}260uER9W3BJp@m7I(pjK3G}k0)~Z_-`u5$Zwn1sn$>Zz4#puP8NN+6Q1VY RH#`LQe#1j9%l+T+{2w6|ym0^k diff --git a/modules/png-extraction/config.json b/modules/png-extraction/config.json index 5e4f2d8..6219a60 100644 --- a/modules/png-extraction/config.json +++ b/modules/png-extraction/config.json @@ -1,13 +1,13 @@ { - "DICOMHome": "/Users/pradeeban/Downloads", - "OutputDirectory": "/Users/pradeeban/Downloads/root", - "Depth": 0, - "SplitIntoChunks": 1, - "PrintImages": true, - "CommonHeadersOnly": false, - "UseProcesses": 0, - "FlattenedToLevel": "patient", - "is16Bit": true, - "SendEmail": true, - "YourEmail": "test@test.test" -} + "DICOMHome": "/Users/pradeeban/Downloads", + "OutputDirectory": "/Users/pradeeban/Downloads/root", + "Depth": 0, + "SplitIntoChunks": 1, + "PrintImages": true, + "CommonHeadersOnly": false, + "UseProcesses": 0, + "FlattenedToLevel": "patient", + "is16Bit":true, + "SendEmail": true, + "YourEmail": "test@test.test" +} \ No newline at end of file From 5c7a97ca6e1c688405b4c11bacf6f57c4fa3ae31 Mon Sep 17 00:00:00 2001 From: Nishchal Singi <71981858+Nishchal-007@users.noreply.github.com> Date: Wed, 9 Jun 2021 20:48:26 +0530 Subject: [PATCH 14/46] Update config.json --- modules/png-extraction/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/png-extraction/config.json b/modules/png-extraction/config.json index 6219a60..3939e71 100644 --- a/modules/png-extraction/config.json +++ b/modules/png-extraction/config.json @@ -10,4 +10,4 @@ "is16Bit":true, "SendEmail": true, "YourEmail": "test@test.test" -} \ No newline at end of file +} From 9a8e33f874d841fb744a37395131a91da4c35806 Mon Sep 17 00:00:00 2001 From: Pradeeban Kathiravelu Date: Wed, 9 Jun 2021 12:58:20 -0400 Subject: [PATCH 15/46] Delete modules/frontend/__pycache__ directory --- .../frontend/__pycache__/server.cpython-38.pyc | Bin 1207 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 modules/frontend/__pycache__/server.cpython-38.pyc diff --git a/modules/frontend/__pycache__/server.cpython-38.pyc b/modules/frontend/__pycache__/server.cpython-38.pyc deleted file mode 100644 index 41b9b345e9e08e9d913901e18638d7cdbf294129..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1207 zcmZvby>HYo6u{%e`M6wi{h~++2^N-HN?DMQDuigS1*!B-P#7$v5S@K@Y2zf=zKYK6 z+J%7`Bo;O#{u{4M{0kir&%Q`h1)u!s#k5{zf_>R6I^F4;)bV8>8NTe-Xk+tb@ltl|^8*l{HDxMNbiLf4F}=u5BnA93Mi@-v#$Y;~;Sp zbRA_G+_uUxf*);eZuSwuO)XUa2C-kuu`pdGOe5L*3*Tsg5vB))X>0A)QsX99GmVex zq9W;NFdXd Date: Sun, 13 Jun 2021 15:52:38 +0530 Subject: [PATCH 16/46] Authentication Added --- modules/frontend/__init__.py | 12 +++ modules/frontend/db.sqlite | Bin 0 -> 12288 bytes modules/frontend/models.py | 13 +++ modules/frontend/server.py | 70 +++++++++++- modules/frontend/static/css/base.css | 143 ++++++++++++++++++++++++ modules/frontend/templates/Home.html | 73 +++++++------ modules/frontend/templates/base.html | 144 +++++++++++++++++++++++++ modules/frontend/templates/login.html | 79 ++++++++++++++ modules/frontend/templates/signup.html | 74 +++++++++++++ 9 files changed, 570 insertions(+), 38 deletions(-) create mode 100644 modules/frontend/db.sqlite create mode 100644 modules/frontend/models.py create mode 100644 modules/frontend/templates/base.html create mode 100644 modules/frontend/templates/login.html create mode 100644 modules/frontend/templates/signup.html diff --git a/modules/frontend/__init__.py b/modules/frontend/__init__.py index e69de29..9306ac5 100644 --- a/modules/frontend/__init__.py +++ b/modules/frontend/__init__.py @@ -0,0 +1,12 @@ +from flask import Flask +import os +from flask_sqlalchemy import SQLAlchemy +import warnings +warnings.filterwarnings("ignore") + +PEOPLE_FOLDER = os.path.join('static','styles') +app = Flask(__name__) + +app.config['SECRET_KEY'] = 'secret-key-goes-here' +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite' +db = SQLAlchemy(app) \ No newline at end of file diff --git a/modules/frontend/db.sqlite b/modules/frontend/db.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..57d5a0227cb96fefb98258d0c4e0b46d041d9964 GIT binary patch literal 12288 zcmeI$O>fgM7yw`=MTJ%xnS`oct*sL+LX(|1UqTbhx`-7@V4+3oE|rrw5Gif9erUk0 z`~?0OXKwoo+&E&FAx&`D5uv@7{Po(2lgB6f%}K9V2%Jx6W4^!^+C-X$4lqUtRj#tE z)=87)ovXI0RR5+_L3>ZXZ5Th0ZfqlC+xU9Z127H(AOHd&00JNY0w4eaAOHd&00RF; zVD(75yVYoDpWOvdO0k@anaX-64ugJ(`@vx^#43VY^;(hPZqyH-g)xp^^>H-l^&aDT zO^kU_;eS#E1ELDbMyJzFbV#{gL|05mlu++uu{38#1rY=3nFi9TEtD z00@8p2!H?xfB*=900@8p2;5qMiC#s!tNlhfE$M+Q_R{Hi{+^q*(=-o1#uvxIS?AO6 z)L$~uBpFSGNHUgM)XSOW*_P!v7I#f5a>sHrKTBLW!xbraJmGL=65q1ZoKxFkxv$nJ zwJO?K?LUz1KbAX>hY^3bFgyHtbb55eo3@#Fo|`(JX?rwxy)5?xWsETD5-SxfO?`(_ z;wFCTuq5{#LVPdrE#?v#&52y1F85V8E|GC*e806@3_lJ6AOHd&00JNY0w4eaAOHd& a00RHHz-DDvQ@0AguM+h8m7UtPtA(E_B#`j{ literal 0 HcmV?d00001 diff --git a/modules/frontend/models.py b/modules/frontend/models.py new file mode 100644 index 0000000..cd9c75e --- /dev/null +++ b/modules/frontend/models.py @@ -0,0 +1,13 @@ +from __init__ import db +from flask import Flask +import os +from flask_sqlalchemy import SQLAlchemy +from flask_login import UserMixin +import warnings +warnings.filterwarnings("ignore") + +class User(UserMixin, db.Model): + id = db.Column(db.Integer, primary_key=True) # primary keys are required by SQLAlchemy + email = db.Column(db.String(100), unique=True) + password = db.Column(db.String(100)) + name = db.Column(db.String(1000)) \ No newline at end of file diff --git a/modules/frontend/server.py b/modules/frontend/server.py index 65b8ec5..2761710 100644 --- a/modules/frontend/server.py +++ b/modules/frontend/server.py @@ -1,20 +1,88 @@ from flask import Flask, flash, request, redirect, url_for, render_template, send_file import os +from flask_sqlalchemy import SQLAlchemy +from flask_login import UserMixin +from flask_login import LoginManager, login_user, login_required, current_user, logout_user +import warnings +warnings.filterwarnings("ignore") + +from werkzeug.security import generate_password_hash, check_password_hash +from __init__ import app, db +from models import User PEOPLE_FOLDER = os.path.join('static','styles') -app = Flask(__name__) +# app = Flask(__name__) + +login_manager = LoginManager(app) +login_manager.login_view = 'login' + +@login_manager.user_loader +def load_user(user_id): + # since the user_id is just the primary key of our user table, use it in the query for the user + return User.query.get(int(user_id)) @app.route("/", methods=['GET']) def index(): return render_template('home.html') +@app.route('/login', methods=['GET','POST']) +def login(): + if request.method == 'POST': + email = request.form.get('email') + password = request.form.get('password') + remember = True if request.form.get('remember') else False + + user = User.query.filter_by(email=email).first() + + # check if the user actually exists + # take the user-supplied password, hash it, and compare it to the hashed password in the database + if not user or not check_password_hash(user.password, password): + flash('Please check your login details and try again.') + return render_template('login.html') # if the user doesn't exist or password is wrong, reload the page + + # if the above check passes, then we know the user has the right credentials + login_user(user, remember=remember) + return render_template('home.html') + return render_template('login.html') + +@app.route('/signup', methods=['GET','POST']) +def signup(): + if request.method =='POST': + email = request.form.get('email') + name = request.form.get('name') + password = request.form.get('password') + + user = User.query.filter_by(email=email).first() # if this returns a user, then the email already exists in database + + if user: # if a user is found, we want to redirect back to signup page so user can try again + flash('Email address already exists') + return render_template('signup.html') + + # create a new user with the form data. Hash the password so the plaintext version isn't saved. + new_user = User(email=email, name=name, password=generate_password_hash(password, method='sha256')) + + # add the new user to the database + db.session.add(new_user) + db.session.commit() + return render_template('login.html') + + return render_template('signup.html') + +@app.route('/logout') +@login_required +def logout(): + logout_user() + return render_template('home.html') + @app.route("/png-extraction", methods = ['GET']) +@login_required def PNG_Extraction(): return render_template('pngHome.html') config_values = {} @app.route('/', methods=['POST']) +@login_required def extract_png(): if request.method =='POST': config_values["dcmFolder"] = request.form['DICOMFolder'] diff --git a/modules/frontend/static/css/base.css b/modules/frontend/static/css/base.css index 84ed14e..f727ff2 100644 --- a/modules/frontend/static/css/base.css +++ b/modules/frontend/static/css/base.css @@ -3,6 +3,11 @@ html { height: 100%; } +::-webkit-scrollbar { + width: 0; /* Remove scrollbar space */ + background: transparent; /* Optional: just make scrollbar invisible */ +} + .nav-item { margin: 8px; padding: 12px; @@ -109,3 +114,141 @@ html { color: white; font-size: 18px; } + +/* Login Styles */ + +.login_background { + background-color: #1a2226; + height: 100%; + width: 100%; + position: absolute; +} + +.login-box { + margin-top: 75px; + height: auto; + background: #1a2226; + text-align: center; + padding: 25px; + border-radius: 15px; + box-shadow: 0 2px 2px rgba(0, 0, 0, 0.8), 10px 13px 15px rgba(0, 0, 0, 0.6); +} + +.login-title { + margin-top: 15px; + text-align: center; + font-size: 30px; + letter-spacing: 2px; + margin-top: 15px; + font-weight: bold; + color: #ecf0f5; +} + +.login-form { + margin-top: 25px; + text-align: left; +} + +.login_background input[type="email"] { + background-color: #1a2226; + border: none; + border-bottom: 2px solid #0db8de; + border-top: 0px; + border-radius: 0px; + font-weight: bold; + outline: 0; + margin-bottom: 20px; + padding-left: 0px; + color: #ecf0f5; +} + +.login_background input[type="text"] { + background-color: #1a2226; + border: none; + border-bottom: 2px solid #0db8de; + border-top: 0px; + border-radius: 0px; + font-weight: bold; + outline: 0; + margin-bottom: 20px; + padding-left: 0px; + color: #ecf0f5; +} + +.login_background input[type="password"] { + background-color: #1a2226; + border: none; + border-bottom: 2px solid #0db8de; + border-top: 0px; + border-radius: 0px; + font-weight: bold; + outline: 0; + padding-left: 0px; + margin-bottom: 20px; + color: #ecf0f5; +} + +.form-group { + margin-bottom: 40px; + outline: 0px; +} + +.form-control:focus { + border-color: inherit; + -webkit-box-shadow: none; + box-shadow: none; + border-bottom: 2px solid #0db8de; + outline: 0; + background-color: #1a2226; + color: #ecf0f5; +} + +.login_background input:focus { + outline: none; + box-shadow: 0 0 0; +} + +.login_background label { + margin-bottom: 0px; +} + +.form-control-label { + font-size: 10px; + color: #6c6c6c; + font-weight: bold; + letter-spacing: 1px; +} + +.login_background .btn-outline-primary { + border-color: #0db8de; + color: #0db8de; + border-radius: 0px; + font-weight: bold; + letter-spacing: 1px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); +} + +.login_background .btn-outline-primary:hover { + background-color: #0db8de; + right: 0px; +} + +.login-btm { + float: left; +} + +.login-button { + padding-right: 0px; + text-align: center; + margin-bottom: 25px; +} + +.login-text { + text-align: left; + padding-left: 0px; + color: #a2a4a4; +} + +.loginbttm { + padding: 0px; +} diff --git a/modules/frontend/templates/Home.html b/modules/frontend/templates/Home.html index c8bdc96..72a3cb4 100644 --- a/modules/frontend/templates/Home.html +++ b/modules/frontend/templates/Home.html @@ -1,10 +1,10 @@ - + +{% extends "base.html" %} {% block content %} +
+
+
+
+

NIFFLER

+
+

+ A DICOM Framework for Machine Learning Pipelines and + Processing Workflows +

+
+
+ +
+
- - + + +{% endblock %} + diff --git a/modules/frontend/templates/base.html b/modules/frontend/templates/base.html new file mode 100644 index 0000000..4339fa7 --- /dev/null +++ b/modules/frontend/templates/base.html @@ -0,0 +1,144 @@ + + + + + + + Niffler + + + + + + + + +
{% block content %} {% endblock %}
+ + diff --git a/modules/frontend/templates/login.html b/modules/frontend/templates/login.html new file mode 100644 index 0000000..f1e504f --- /dev/null +++ b/modules/frontend/templates/login.html @@ -0,0 +1,79 @@ +{% extends "base.html" %} {% block content %} + + diff --git a/modules/frontend/templates/signup.html b/modules/frontend/templates/signup.html new file mode 100644 index 0000000..5cfb248 --- /dev/null +++ b/modules/frontend/templates/signup.html @@ -0,0 +1,74 @@ +{% extends "base.html" %} {% block content %} + + From 81993f86c801b229f35e50d1b553c9bfc8c22f50 Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Mon, 14 Jun 2021 16:00:19 +0530 Subject: [PATCH 17/46] Started cold extraction --- modules/frontend/db.sqlite | Bin 12288 -> 12288 bytes modules/frontend/server.py | 26 +- modules/frontend/static/css/base.css | 15 +- modules/frontend/templates/base.html | 2 +- .../frontend/templates/cold_extraction.html | 241 ++++++++++ modules/frontend/templates/login.html | 3 +- modules/frontend/templates/pngHome.html | 451 +++++++++--------- modules/frontend/templates/signup.html | 11 +- 8 files changed, 508 insertions(+), 241 deletions(-) create mode 100644 modules/frontend/templates/cold_extraction.html diff --git a/modules/frontend/db.sqlite b/modules/frontend/db.sqlite index 57d5a0227cb96fefb98258d0c4e0b46d041d9964..d34af52e629005fcb503b6919d1be940c8d7d7f5 100644 GIT binary patch delta 203 zcmZojXh@hK&B!uQ#+i|2W5N=CE@r+>4E&S$mH0MoEL_4@pU2F`px$UJoRbfP4nVAz zoS$2qk!WOUrjnRu8c-SG=9E{SXQpCmZen4aXlZ1Ym~4@3Vq}tJmS|{cm}Z!k zY;I|>Xli0^YMKZ%K97lwLAB9V6v#9L pS~uBH{xTOc|2YQ!2mBxT&utb|IKVF?$gIu?lt;LSdGZ5&1pwHeJmvrZ delta 56 zcmZojXh@hK&B#1a#+i|MW5N=C4kmsj2L4I>N}B~0#P}yG$v*;$JYe8|!2b~_vX_5i KgZShJ`U(K9vk-Ow diff --git a/modules/frontend/server.py b/modules/frontend/server.py index 2761710..8d57535 100644 --- a/modules/frontend/server.py +++ b/modules/frontend/server.py @@ -74,14 +74,14 @@ def logout(): logout_user() return render_template('home.html') -@app.route("/png-extraction", methods = ['GET']) -@login_required -def PNG_Extraction(): - return render_template('pngHome.html') +# @app.route("/png-extraction", methods = ['GET']) +# @login_required +# def PNG_Extraction(): +# return render_template('pngHome.html') config_values = {} -@app.route('/', methods=['POST']) +@app.route('/png-extraction', methods=['GET', 'POST']) @login_required def extract_png(): if request.method =='POST': @@ -105,6 +105,22 @@ def extract_png(): return render_template('pngHome.html', logs = lt) return render_template('pngHome.html') +@app.route('/cold-extraction', methods=['GET', 'POST']) +@login_required +def cold_extraction(): + if request.method =='POST': + csv_file = request.files['csvFile'] + + import csv + import io + + stream = io.StringIO(csv_file.stream.read().decode("UTF8"), newline=None) + csv_input = csv.reader(stream) + # for row in csv_input: + # print(row) + + return render_template('cold_extraction.html') + return render_template('cold_extraction.html') #JUST DO IT!!! if __name__=="__main__": app.run(port="9000") \ No newline at end of file diff --git a/modules/frontend/static/css/base.css b/modules/frontend/static/css/base.css index f727ff2..3481aa8 100644 --- a/modules/frontend/static/css/base.css +++ b/modules/frontend/static/css/base.css @@ -98,12 +98,13 @@ html { /* PNG Extraction Styles */ .backdrop { - /* position: absolute; */ background-image: url("../images/img1.png"); height: 100%; + width: 100%; background-position: center; background-repeat: no-repeat; background-size: cover; + position: absolute; } .row { @@ -244,11 +245,15 @@ html { } .login-text { - text-align: left; - padding-left: 0px; - color: #a2a4a4; + background-color: red; + margin: 12px; + border-radius: 12px; + padding: 10px; + color: white; + text-align: center; + font-size: 22px; } .loginbttm { - padding: 0px; + padding: 25px; } diff --git a/modules/frontend/templates/base.html b/modules/frontend/templates/base.html index 4339fa7..c6a06ee 100644 --- a/modules/frontend/templates/base.html +++ b/modules/frontend/templates/base.html @@ -115,7 +115,7 @@ " >

-
-
+
+
+ +
+
DICOM Home Folder
+
+
-
-
+
+
+ +
+
To 16-bit
- + /> --> +
-
-
+
+
+ +
+
Print Images
- + /> --> +
-
-
+
+
+ +
+
Common Headers Only
- + /> --> +
-
-
+
+
+ +
+
Send Email
- + /> --> +
+ -
+
{% endblock %} - diff --git a/modules/png-extraction/ImageExtractor.py b/modules/png-extraction/ImageExtractor.py index d0c152b..2a66d2f 100644 --- a/modules/png-extraction/ImageExtractor.py +++ b/modules/png-extraction/ImageExtractor.py @@ -431,6 +431,7 @@ def Execute(pickle_file, dicom_home, output_directory, print_images, print_only_ subprocess.call('echo "Niffler has successfully completed the png conversion" | mail -s "The image conversion has been complete" {0}'.format(email), shell=True) # Record the total run-time logging.info('Total run time: %s %s', time.time() - t_start, ' seconds!') + logging.shutdown() # Closing logging file after extraction is done !! logs = [] logs.append(err) logs.append("The PNG conversion is SUCCESSFUL") From be0afee0daafe8af3910dbc75a5bb3dd3e5e7ef4 Mon Sep 17 00:00:00 2001 From: Thomas Kim Date: Thu, 17 Jun 2021 08:30:10 -0400 Subject: [PATCH 20/46] modified conditions used to determine which metadata headers to keep; now use cohort-level threshold instead of batch-level threshold in attempt to maintain consistency across cohorts --- modules/png-extraction/ImageExtractor.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/png-extraction/ImageExtractor.py b/modules/png-extraction/ImageExtractor.py index d0c152b..0c6ec29 100644 --- a/modules/png-extraction/ImageExtractor.py +++ b/modules/png-extraction/ImageExtractor.py @@ -384,18 +384,23 @@ def Execute(pickle_file, dicom_home, output_directory, print_images, print_only_ #identify the col_names = dict() all_headers = dict() + total_length = 0 metas = glob.glob( "{}*.csv".format(meta_directory)) #for each meta file identify the columns that are not na's for at least 10% (metadata_col_freq_threshold) of data for meta in metas: m = pd.read_csv(meta,dtype='str') d_len = m.shape[0] + total_length += d_len + for e in m.columns: - if np.sum(m[e].isna()) < (1-metadata_col_freq_threshold)*d_len: # Column e is populated in at least 10% of rows (i.e. isNaN in <90% of rows) - if e in col_names: - col_names[e] += 1 - else: - col_names[e] = 1 + col_pop = d_len - np.sum(m[e].isna()) # number of populated rows for this column in this metadata file + + if e in col_names: + col_names[e] += col_pop + else: + col_names[e] = col_pop + # all_headers keeps track of number of appearances of each header. We later use this count to ensure that the headers we use are present in all metadata files. if e in all_headers: all_headers[e] += 1 @@ -405,7 +410,8 @@ def Execute(pickle_file, dicom_home, output_directory, print_images, print_only_ loadable_names = list() for k in col_names.keys(): if k in all_headers and all_headers[k] >= no_splits: # no_splits == number of batches used - loadable_names.append(k) # use header only if it's present in every metadata file + if col_names[k] >= metadata_col_freq_threshold*total_length: + loadable_names.append(k) # use header only if it's present in every metadata file #load every metadata file using only valid columns meta_list = list() From cc257f6b64dc68f9d52856d80a63fd7e58acbe36 Mon Sep 17 00:00:00 2001 From: Chinmay Vibhute Date: Thu, 17 Jun 2021 22:31:37 +0530 Subject: [PATCH 21/46] test init and png-extraction unit tests --- .gitignore | 6 + modules/frontend/server.py | 2 +- requirements-dev.txt | 13 ++ tests/README.md | 34 +++ tests/conftest.py | 15 ++ tests/data/png-extraction/input/.gitkeep | 0 tests/data/png-extraction/input/no-img.dcm | 0 .../png-extraction/no_input_files/.gitkeep | 0 tests/unit/test_png_extraction.py | 217 ++++++++++++++++++ 9 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 requirements-dev.txt create mode 100644 tests/README.md create mode 100644 tests/conftest.py create mode 100644 tests/data/png-extraction/input/.gitkeep create mode 100644 tests/data/png-extraction/input/no-img.dcm create mode 100644 tests/data/png-extraction/no_input_files/.gitkeep create mode 100644 tests/unit/test_png_extraction.py diff --git a/.gitignore b/.gitignore index 3c9dc5b..f4858d0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,9 @@ target/ **/*.csv __pycache__ **/*.pyc +htmlcov +.coverage +coverage.xml +/tests/data/tmp +/tests/data/**/*.dcm +!/tests/data/**/no-img.dcm \ No newline at end of file diff --git a/modules/frontend/server.py b/modules/frontend/server.py index c85ee87..c5d30ca 100644 --- a/modules/frontend/server.py +++ b/modules/frontend/server.py @@ -24,7 +24,7 @@ def extract_png(): config_values["CommonHeadersOnly"] = request.form['headers'] config_values["SendEmail"] = request.form['sendEmail'] config_values["YourEmail"] = request.form['email'] - print(config_values) + if(len(config_values) > 0): import sys sys.path.append("../png-extraction/") diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..183f02e --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,13 @@ +requests +pymongo +schedule +pydicom +pynetdicom +image +numpy +pandas +pillow +pypng +pytest +pytest-mock +pytest-cov diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..3580cfe --- /dev/null +++ b/tests/README.md @@ -0,0 +1,34 @@ +# Testing Framework for Niffler Modules + +## Setup + +Install the requirements from `/requirements-dev.txt` + +``` +pip install -r requirements-dev.txt +``` + +Add the test data in `/tests/data//input` for respective tests. + +### PNG Extraction Data Setup + +Test data in `/tests/data/png-extraction/input`. + +For unit tests, add a valid dcm file, with name `test-img.dcm`. + +## Running Tests + +Initialize the required data, and run tests from ``. + +```bash +pytest ./tests +``` + +For coverage report, run + +```bash +pytest ./tests --cov=./modules --cov-report=html +``` + +and open the `/htmlcov/index.html` + diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..ba961c6 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,15 @@ +import os +import pytest +from pathlib import Path + + +def create_dirs(*args): + for dir in args: + if not os.path.exists(dir): + os.makedirs(dir) + + +def pytest_configure(): + pytest.data_dir = Path.cwd() / 'tests' / 'data' + pytest.out_dir = Path.cwd() / 'tests' / 'data' / 'tmp' / 'niffler-tests' + pytest.create_dirs = create_dirs diff --git a/tests/data/png-extraction/input/.gitkeep b/tests/data/png-extraction/input/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/data/png-extraction/input/no-img.dcm b/tests/data/png-extraction/input/no-img.dcm new file mode 100644 index 0000000..e69de29 diff --git a/tests/data/png-extraction/no_input_files/.gitkeep b/tests/data/png-extraction/no_input_files/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/test_png_extraction.py b/tests/unit/test_png_extraction.py new file mode 100644 index 0000000..efe0e25 --- /dev/null +++ b/tests/unit/test_png_extraction.py @@ -0,0 +1,217 @@ +import glob +import pytest +import sys +import time + +from pathlib import Path, PurePath +from pytest_mock import MockerFixture + +# Import Niffler Module +niffler_modules_path = Path.cwd() / 'modules' +sys.path.append(str(niffler_modules_path / 'png-extraction')) +import ImageExtractor + +import pydicom +import pandas as pd + + +@pytest.fixture +def mock_pydicom_config_data_element_callback(mocker: MockerFixture): + return mocker.patch.object(pydicom.config, 'data_element_callback') + + +@pytest.fixture +def mock_pydicom_config_data_element_callback_kwargs(mocker: MockerFixture): + return mocker.patch.object(pydicom.config, 'data_element_callback_kwargs') + + +@pytest.fixture +def mock_logger(mocker: MockerFixture): + return mocker.patch('ImageExtractor.logging') + + +class TestGetPath: + dicom_home = "/mock/path/to/dicom/home" + + def test_get_path_zero_depth(self): + depth = 0 + dcm_path = ImageExtractor.get_path(depth, self.dicom_home) + assert dcm_path == f"{self.dicom_home}/*.dcm" + + def test_get_path_some_depth(self): + depth = 3 + dcm_path = ImageExtractor.get_path(depth, self.dicom_home) + assert dcm_path == f"{self.dicom_home}{''.join(['/*']*depth)}/*.dcm" + + +class TestFixMismatch: + with_VRs = ['PN', 'DS', 'IS'] + + def test_fix_mismatch(self, mock_pydicom_config_data_element_callback, mock_pydicom_config_data_element_callback_kwargs): + ImageExtractor.fix_mismatch(with_VRs=self.with_VRs) + assert pydicom.config.data_element_callback is ImageExtractor.fix_mismatch_callback + assert pydicom.config.data_element_callback_kwargs['with_VRs'] == self.with_VRs + + +class TestExtractHeaders: + valid_test_dcm_file = 0, str( + pytest.data_dir / 'png-extraction' / 'input' / 'test-img.dcm') + invalid_test_dcm_file = 0, str( + pytest.data_dir / 'png-extraction' / 'input' / 'no-img.dcm') + + def test_no_image(self): + headers = ImageExtractor.extract_headers( + self.invalid_test_dcm_file) + assert headers['has_pix_array'] is False + + def test_valid_image(self): + headers = ImageExtractor.extract_headers(self.valid_test_dcm_file) + assert headers['has_pix_array'] is True + + # TODO large dcm files + + +class TestGetTuples: + test_dcm_file = str( + pytest.data_dir / 'png-extraction' / 'input' / 'test-img.dcm') + test_valid_plan = pydicom.dcmread(test_dcm_file, force=True) + + def test_correct_output(self): + first_key = self.test_valid_plan.dir()[0] + tuple_list = ImageExtractor.get_tuples(self.test_valid_plan) + assert tuple_list[0][0] == first_key + + # TODO hasattr error + # TODO large dcm files + + +class TestExtractImages: + + # TODO Write suitable assertions + + test_dcm_file = str( + pytest.data_dir / 'png-extraction' / 'input' / 'test-img.dcm') + invalid_test_dcm_file = str( + pytest.data_dir / 'png-extraction' / 'input' / 'no-img.dcm') + + def setup_method(self): + header_list = [ImageExtractor.extract_headers( + (0, self.test_dcm_file))] + self.file_data = pd.DataFrame(header_list) + self.index = 0 + self.invalid_file_data = pd.DataFrame([ + { + 'some_col_1': 'Dummy Col1 Value', + 'some_col_2': 'Dummy Col2 Value', + 'file': self.invalid_test_dcm_file + } + ]) + out_dir = pytest.out_dir / 'png-extraction/outputs/TestExtractImages' + self.png_destination = f"{str(out_dir)}/extracted-images/" + self.failed = f"{str(out_dir)}/failed-dicom/" + pytest.create_dirs(out_dir, self.png_destination, self.failed) + + def test_is16bit(self): + flattened_to_level = "patient" + is16Bit = "True" + out_img = ImageExtractor.extract_images( + self.file_data, + self.index, + self.png_destination, + flattened_to_level, + self.failed, + is16Bit + ) + assert out_img[0].startswith(self.test_dcm_file) + + def test_not_is16bit(self): + flattened_to_level = "patient" + is16Bit = "False" + out_img = ImageExtractor.extract_images( + self.file_data, + self.index, + self.png_destination, + flattened_to_level, + self.failed, + is16Bit + ) + assert out_img[0].startswith(self.test_dcm_file) + + def test_level_patient(self): + flattened_to_level = "patient" + is16Bit = "False" + out_img = ImageExtractor.extract_images( + self.file_data, + self.index, + self.png_destination, + flattened_to_level, + self.failed, + is16Bit + ) + assert out_img[0].startswith(self.test_dcm_file) + + def test_level_study(self): + flattened_to_level = "study" + is16Bit = "False" + out_img = ImageExtractor.extract_images( + self.file_data, + self.index, + self.png_destination, + flattened_to_level, + self.failed, + is16Bit + ) + assert out_img[0].startswith(self.test_dcm_file) + + def test_level_other(self): + flattened_to_level = "other" + is16Bit = "False" + out_img = ImageExtractor.extract_images( + self.file_data, + self.index, + self.png_destination, + flattened_to_level, + self.failed, + is16Bit + ) + assert out_img[0].startswith(self.test_dcm_file) + + def test_level_other_no_study_uuid(self): + flattened_to_level = "other" + is16Bit = "False" + out_img = ImageExtractor.extract_images( + self.file_data.drop(['StudyInstanceUID'], axis=1), + self.index, + self.png_destination, + flattened_to_level, + self.failed, + is16Bit + ) + assert out_img[0].startswith(self.test_dcm_file) + + def test_level_study_no_study_uuid(self): + flattened_to_level = "study" + is16Bit = "False" + out_img = ImageExtractor.extract_images( + self.file_data.drop(['StudyInstanceUID'], axis=1), + self.index, + self.png_destination, + flattened_to_level, + self.failed, + is16Bit + ) + assert out_img[0].startswith(self.test_dcm_file) + + def test_failed_read_attrerr(self): + flattened_to_level = "study" + is16Bit = "False" + out_img = ImageExtractor.extract_images( + self.invalid_file_data, + self.index, + self.png_destination, + flattened_to_level, + self.failed, + is16Bit + ) + assert out_img[1][0] == self.invalid_test_dcm_file + assert out_img[2] is not None From ba205422df07ca61f7deca88c54e777a1230199d Mon Sep 17 00:00:00 2001 From: Chinmay Vibhute Date: Fri, 18 Jun 2021 00:31:06 +0530 Subject: [PATCH 22/46] unit tests for png-extraction --- .gitignore | 6 + requirements-dev.txt | 13 ++ tests/README.md | 34 +++ tests/conftest.py | 15 ++ tests/data/png-extraction/input/.gitkeep | 0 tests/data/png-extraction/input/no-img.dcm | 0 .../png-extraction/no_input_files/.gitkeep | 0 tests/unit/test_png_extraction.py | 217 ++++++++++++++++++ 8 files changed, 285 insertions(+) create mode 100644 requirements-dev.txt create mode 100644 tests/README.md create mode 100644 tests/conftest.py create mode 100644 tests/data/png-extraction/input/.gitkeep create mode 100644 tests/data/png-extraction/input/no-img.dcm create mode 100644 tests/data/png-extraction/no_input_files/.gitkeep create mode 100644 tests/unit/test_png_extraction.py diff --git a/.gitignore b/.gitignore index 3c9dc5b..f4858d0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,9 @@ target/ **/*.csv __pycache__ **/*.pyc +htmlcov +.coverage +coverage.xml +/tests/data/tmp +/tests/data/**/*.dcm +!/tests/data/**/no-img.dcm \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..183f02e --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,13 @@ +requests +pymongo +schedule +pydicom +pynetdicom +image +numpy +pandas +pillow +pypng +pytest +pytest-mock +pytest-cov diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..3580cfe --- /dev/null +++ b/tests/README.md @@ -0,0 +1,34 @@ +# Testing Framework for Niffler Modules + +## Setup + +Install the requirements from `/requirements-dev.txt` + +``` +pip install -r requirements-dev.txt +``` + +Add the test data in `/tests/data//input` for respective tests. + +### PNG Extraction Data Setup + +Test data in `/tests/data/png-extraction/input`. + +For unit tests, add a valid dcm file, with name `test-img.dcm`. + +## Running Tests + +Initialize the required data, and run tests from ``. + +```bash +pytest ./tests +``` + +For coverage report, run + +```bash +pytest ./tests --cov=./modules --cov-report=html +``` + +and open the `/htmlcov/index.html` + diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..ba961c6 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,15 @@ +import os +import pytest +from pathlib import Path + + +def create_dirs(*args): + for dir in args: + if not os.path.exists(dir): + os.makedirs(dir) + + +def pytest_configure(): + pytest.data_dir = Path.cwd() / 'tests' / 'data' + pytest.out_dir = Path.cwd() / 'tests' / 'data' / 'tmp' / 'niffler-tests' + pytest.create_dirs = create_dirs diff --git a/tests/data/png-extraction/input/.gitkeep b/tests/data/png-extraction/input/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/data/png-extraction/input/no-img.dcm b/tests/data/png-extraction/input/no-img.dcm new file mode 100644 index 0000000..e69de29 diff --git a/tests/data/png-extraction/no_input_files/.gitkeep b/tests/data/png-extraction/no_input_files/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/test_png_extraction.py b/tests/unit/test_png_extraction.py new file mode 100644 index 0000000..efe0e25 --- /dev/null +++ b/tests/unit/test_png_extraction.py @@ -0,0 +1,217 @@ +import glob +import pytest +import sys +import time + +from pathlib import Path, PurePath +from pytest_mock import MockerFixture + +# Import Niffler Module +niffler_modules_path = Path.cwd() / 'modules' +sys.path.append(str(niffler_modules_path / 'png-extraction')) +import ImageExtractor + +import pydicom +import pandas as pd + + +@pytest.fixture +def mock_pydicom_config_data_element_callback(mocker: MockerFixture): + return mocker.patch.object(pydicom.config, 'data_element_callback') + + +@pytest.fixture +def mock_pydicom_config_data_element_callback_kwargs(mocker: MockerFixture): + return mocker.patch.object(pydicom.config, 'data_element_callback_kwargs') + + +@pytest.fixture +def mock_logger(mocker: MockerFixture): + return mocker.patch('ImageExtractor.logging') + + +class TestGetPath: + dicom_home = "/mock/path/to/dicom/home" + + def test_get_path_zero_depth(self): + depth = 0 + dcm_path = ImageExtractor.get_path(depth, self.dicom_home) + assert dcm_path == f"{self.dicom_home}/*.dcm" + + def test_get_path_some_depth(self): + depth = 3 + dcm_path = ImageExtractor.get_path(depth, self.dicom_home) + assert dcm_path == f"{self.dicom_home}{''.join(['/*']*depth)}/*.dcm" + + +class TestFixMismatch: + with_VRs = ['PN', 'DS', 'IS'] + + def test_fix_mismatch(self, mock_pydicom_config_data_element_callback, mock_pydicom_config_data_element_callback_kwargs): + ImageExtractor.fix_mismatch(with_VRs=self.with_VRs) + assert pydicom.config.data_element_callback is ImageExtractor.fix_mismatch_callback + assert pydicom.config.data_element_callback_kwargs['with_VRs'] == self.with_VRs + + +class TestExtractHeaders: + valid_test_dcm_file = 0, str( + pytest.data_dir / 'png-extraction' / 'input' / 'test-img.dcm') + invalid_test_dcm_file = 0, str( + pytest.data_dir / 'png-extraction' / 'input' / 'no-img.dcm') + + def test_no_image(self): + headers = ImageExtractor.extract_headers( + self.invalid_test_dcm_file) + assert headers['has_pix_array'] is False + + def test_valid_image(self): + headers = ImageExtractor.extract_headers(self.valid_test_dcm_file) + assert headers['has_pix_array'] is True + + # TODO large dcm files + + +class TestGetTuples: + test_dcm_file = str( + pytest.data_dir / 'png-extraction' / 'input' / 'test-img.dcm') + test_valid_plan = pydicom.dcmread(test_dcm_file, force=True) + + def test_correct_output(self): + first_key = self.test_valid_plan.dir()[0] + tuple_list = ImageExtractor.get_tuples(self.test_valid_plan) + assert tuple_list[0][0] == first_key + + # TODO hasattr error + # TODO large dcm files + + +class TestExtractImages: + + # TODO Write suitable assertions + + test_dcm_file = str( + pytest.data_dir / 'png-extraction' / 'input' / 'test-img.dcm') + invalid_test_dcm_file = str( + pytest.data_dir / 'png-extraction' / 'input' / 'no-img.dcm') + + def setup_method(self): + header_list = [ImageExtractor.extract_headers( + (0, self.test_dcm_file))] + self.file_data = pd.DataFrame(header_list) + self.index = 0 + self.invalid_file_data = pd.DataFrame([ + { + 'some_col_1': 'Dummy Col1 Value', + 'some_col_2': 'Dummy Col2 Value', + 'file': self.invalid_test_dcm_file + } + ]) + out_dir = pytest.out_dir / 'png-extraction/outputs/TestExtractImages' + self.png_destination = f"{str(out_dir)}/extracted-images/" + self.failed = f"{str(out_dir)}/failed-dicom/" + pytest.create_dirs(out_dir, self.png_destination, self.failed) + + def test_is16bit(self): + flattened_to_level = "patient" + is16Bit = "True" + out_img = ImageExtractor.extract_images( + self.file_data, + self.index, + self.png_destination, + flattened_to_level, + self.failed, + is16Bit + ) + assert out_img[0].startswith(self.test_dcm_file) + + def test_not_is16bit(self): + flattened_to_level = "patient" + is16Bit = "False" + out_img = ImageExtractor.extract_images( + self.file_data, + self.index, + self.png_destination, + flattened_to_level, + self.failed, + is16Bit + ) + assert out_img[0].startswith(self.test_dcm_file) + + def test_level_patient(self): + flattened_to_level = "patient" + is16Bit = "False" + out_img = ImageExtractor.extract_images( + self.file_data, + self.index, + self.png_destination, + flattened_to_level, + self.failed, + is16Bit + ) + assert out_img[0].startswith(self.test_dcm_file) + + def test_level_study(self): + flattened_to_level = "study" + is16Bit = "False" + out_img = ImageExtractor.extract_images( + self.file_data, + self.index, + self.png_destination, + flattened_to_level, + self.failed, + is16Bit + ) + assert out_img[0].startswith(self.test_dcm_file) + + def test_level_other(self): + flattened_to_level = "other" + is16Bit = "False" + out_img = ImageExtractor.extract_images( + self.file_data, + self.index, + self.png_destination, + flattened_to_level, + self.failed, + is16Bit + ) + assert out_img[0].startswith(self.test_dcm_file) + + def test_level_other_no_study_uuid(self): + flattened_to_level = "other" + is16Bit = "False" + out_img = ImageExtractor.extract_images( + self.file_data.drop(['StudyInstanceUID'], axis=1), + self.index, + self.png_destination, + flattened_to_level, + self.failed, + is16Bit + ) + assert out_img[0].startswith(self.test_dcm_file) + + def test_level_study_no_study_uuid(self): + flattened_to_level = "study" + is16Bit = "False" + out_img = ImageExtractor.extract_images( + self.file_data.drop(['StudyInstanceUID'], axis=1), + self.index, + self.png_destination, + flattened_to_level, + self.failed, + is16Bit + ) + assert out_img[0].startswith(self.test_dcm_file) + + def test_failed_read_attrerr(self): + flattened_to_level = "study" + is16Bit = "False" + out_img = ImageExtractor.extract_images( + self.invalid_file_data, + self.index, + self.png_destination, + flattened_to_level, + self.failed, + is16Bit + ) + assert out_img[1][0] == self.invalid_test_dcm_file + assert out_img[2] is not None From 13f60d06ad997710eb5fa722d080da3ed36e9945 Mon Sep 17 00:00:00 2001 From: Ananth Reddy Date: Fri, 18 Jun 2021 15:38:32 -0400 Subject: [PATCH 23/46] Adding system.json for rta-extraction --- modules/rta-extraction/service/system.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 modules/rta-extraction/service/system.json diff --git a/modules/rta-extraction/service/system.json b/modules/rta-extraction/service/system.json new file mode 100644 index 0000000..e38230f --- /dev/null +++ b/modules/rta-extraction/service/system.json @@ -0,0 +1,9 @@ +{ + "LabsFilePath": "/Users/ananthreddy/Documents/emory_projects/json_parsing/labs/", + "MedsFilePath": "/Users/ananthreddy/Documents/emory_projects/json_parsing/meds/", + "OrdersFilePath": "/Users/ananthreddy/Documents/emory_projects/json_parsing/orders/", + "LabsDataClearFrequency": 30, + "MedsDataClearFrequency": 30, + "OrdersDataClearFrequency": 30, + "MongoURI": "mongodb://localhost:27017" +} From 58cc0d5904244ef44e7dbd7751511b970560be71 Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Sun, 20 Jun 2021 10:18:53 +0530 Subject: [PATCH 24/46] Signup access only to admin --- modules/frontend/__init__.py | 26 ++++++++++++++++++++++++-- modules/frontend/server.py | 14 ++++++++------ modules/frontend/templates/base.html | 4 +++- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/modules/frontend/__init__.py b/modules/frontend/__init__.py index 9306ac5..6ed0414 100644 --- a/modules/frontend/__init__.py +++ b/modules/frontend/__init__.py @@ -1,5 +1,7 @@ -from flask import Flask +from flask import Flask, render_template, flash import os +import sys +from functools import wraps from flask_sqlalchemy import SQLAlchemy import warnings warnings.filterwarnings("ignore") @@ -9,4 +11,24 @@ app.config['SECRET_KEY'] = 'secret-key-goes-here' app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite' -db = SQLAlchemy(app) \ No newline at end of file +db = SQLAlchemy(app) + +isAdmin = False +if(len(sys.argv) > 1 and sys.argv[1] == '--admin'): + isAdmin = True +else: + isAdmin = False + +def checkAdmin(func): + @wraps(func) + def decorated_function(*args, **kwargs): + isAdmin = False + if(len(sys.argv) > 1 and sys.argv[1] == '--admin'): + isAdmin = True + else: + isAdmin = False + if(isAdmin == False): + flash("Sorry, You do not have permission to access this page\nPlease contact admin") + return render_template('login.html') + return func(*args, **kwargs) + return decorated_function \ No newline at end of file diff --git a/modules/frontend/server.py b/modules/frontend/server.py index 616fa92..134b731 100644 --- a/modules/frontend/server.py +++ b/modules/frontend/server.py @@ -1,5 +1,6 @@ from flask import Flask, flash, request, redirect, url_for, render_template, send_file import os +import sys from flask_sqlalchemy import SQLAlchemy from flask_login import UserMixin from flask_login import LoginManager, login_user, login_required, current_user, logout_user @@ -7,7 +8,7 @@ warnings.filterwarnings("ignore") from werkzeug.security import generate_password_hash, check_password_hash -from __init__ import app, db +from __init__ import app, db, isAdmin, checkAdmin from models import User PEOPLE_FOLDER = os.path.join('static','styles') @@ -23,7 +24,7 @@ def load_user(user_id): @app.route("/", methods=['GET']) def index(): - return render_template('home.html') + return render_template('home.html', isAdmin = isAdmin) @app.route('/login', methods=['GET','POST']) def login(): @@ -43,9 +44,10 @@ def login(): # if the above check passes, then we know the user has the right credentials login_user(user, remember=remember) return render_template('home.html') - return render_template('login.html') + return render_template('login.html', isAdmin = isAdmin) @app.route('/signup', methods=['GET','POST']) +@checkAdmin def signup(): if request.method =='POST': email = request.form.get('email') @@ -64,15 +66,15 @@ def signup(): # add the new user to the database db.session.add(new_user) db.session.commit() - return render_template('login.html') + return render_template('login.html', isAdmin = isAdmin) - return render_template('signup.html') + return render_template('signup.html', isAdmin = isAdmin) @app.route('/logout') @login_required def logout(): logout_user() - return render_template('home.html') + return render_template('home.html', isAdmin = isAdmin) # @app.route("/png-extraction", methods = ['GET']) # @login_required diff --git a/modules/frontend/templates/base.html b/modules/frontend/templates/base.html index 0bb0fed..437b3c6 100644 --- a/modules/frontend/templates/base.html +++ b/modules/frontend/templates/base.html @@ -127,10 +127,12 @@ + {% if isAdmin %} - {% endif %} {% if current_user.is_authenticated %} + {% endif %} {% endif %} {% if + current_user.is_authenticated %} From b1ff95f6d1d8fda2fb3033245a7a2a3ce5ea0752 Mon Sep 17 00:00:00 2001 From: Nishchal Singi <71981858+Nishchal-007@users.noreply.github.com> Date: Sun, 20 Jun 2021 10:37:51 +0530 Subject: [PATCH 25/46] Update README.md --- modules/frontend/README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/frontend/README.md b/modules/frontend/README.md index 1e55781..2424d95 100644 --- a/modules/frontend/README.md +++ b/modules/frontend/README.md @@ -8,10 +8,18 @@ This module is using **Flask** as an engine to run all the frontend. ## Steps for running Frontend Module 1. Run server.py file by navigating into frontend directory and running: -`python server.py` or `python3 server.py` +`python server.py` or `python3 server.py` (user level access) 2. Then navigate to `localhost:9000` to view your Niffler modules. +## Admin level access + +1. Run server.py file by navigating into frontend directory and running: +`python server.py --admin` or `python3 server.py --admin` (admin level access) + +2. The admin access enables only admins to create new users. + + *Currently PNG extraction Frontend is developed and it can take values from frontend and can be passed into backend* From 8cc647f8b6a731b74fb7790d5673d5356d7e0008 Mon Sep 17 00:00:00 2001 From: Ananth Reddy Date: Sun, 20 Jun 2021 11:59:25 -0400 Subject: [PATCH 26/46] Adding system.json for rta-extraction --- modules/rta-extraction/service/system.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/rta-extraction/service/system.json b/modules/rta-extraction/service/system.json index e38230f..249df19 100644 --- a/modules/rta-extraction/service/system.json +++ b/modules/rta-extraction/service/system.json @@ -2,8 +2,9 @@ "LabsFilePath": "/Users/ananthreddy/Documents/emory_projects/json_parsing/labs/", "MedsFilePath": "/Users/ananthreddy/Documents/emory_projects/json_parsing/meds/", "OrdersFilePath": "/Users/ananthreddy/Documents/emory_projects/json_parsing/orders/", - "LabsDataClearFrequency": 30, - "MedsDataClearFrequency": 30, - "OrdersDataClearFrequency": 30, + "LabsDataExtractionFrequency": 30, + "MedsDataExtractionFrequency": 30, + "OrdersDataExtractionFrequency": 30, + "MongoURI": "mongodb://localhost:27017" } From efba9a1558ccfe8e198db0748b976918f60fbe05 Mon Sep 17 00:00:00 2001 From: Ananth Reddy Date: Tue, 22 Jun 2021 09:41:50 -0400 Subject: [PATCH 27/46] Adding extractor shell file for rta-extraction --- modules/rta-extraction/service/mdextractor.sh | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100755 modules/rta-extraction/service/mdextractor.sh diff --git a/modules/rta-extraction/service/mdextractor.sh b/modules/rta-extraction/service/mdextractor.sh new file mode 100755 index 0000000..94d659f --- /dev/null +++ b/modules/rta-extraction/service/mdextractor.sh @@ -0,0 +1,7 @@ +#!/bin/bash +cd /opt/localdrive/Niffler/modules/rta-extraction/ +source ~/.bashrc +python3 RtaExtractor.py >> niffler-rta-extraction.out & +wait +echo "The Niffler RTA Extractor Process has failed" >> niffler-rta-extraction.out +echo "The Niffler RTA Extractor Process has failed" | mail -s "The Niffler RTA Extractor Process has failed" test@test.test From 51f781093d856206e88ad2c34bd6242f284920fb Mon Sep 17 00:00:00 2001 From: Ananth Reddy Date: Tue, 22 Jun 2021 09:50:51 -0400 Subject: [PATCH 28/46] Adding functionality for rta-extraction --- modules/rta-extraction/RtaExtractor.py | 91 ++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 modules/rta-extraction/RtaExtractor.py diff --git a/modules/rta-extraction/RtaExtractor.py b/modules/rta-extraction/RtaExtractor.py new file mode 100644 index 0000000..86629ac --- /dev/null +++ b/modules/rta-extraction/RtaExtractor.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os, os.path +import sys +import glob +import pymongo +import pydicom +import requests +import schedule +import time +import datetime +import threading +import logging +import pickle +import shutil +import subprocess +import pandas as pd +import json +from pymongo import MongoClient + +with open('service/system.json', 'r') as f: + niffler = json.load(f) + +# Get constants from system.json +Labs_FolderPath = niffler['LabsFilePath'] +Meds_FolderPath = niffler['MedsFilePath'] +Orders_FolderPath = niffler['OrdersFilePath'] +Labs_ExtractionFrequency = niffler['LabsDataExtractionFrequency'] +Meds_ExtractionFrequency = niffler['MedsDataExtractionFrequency'] +Orders_ExtractionFrequency = niffler['OrdersDataExtractionFrequency'] +Mongo_URI = niffler['MongoURI'] + +# Connect to MongoDB +try: + client = MongoClient(Mongo_URI) + logging.info('MongoDB Connection Successful.') +except: + logging.info('MongoDB Connection Unsuccessful.') + +db = client.database + +def dump_labs_json_data(): + labs_collection = db.labs_json + for file in os.listdir(Labs_FolderPath): + if file.endswith('.json'): + Labs_FilePath = Labs_FolderPath+file + + f = open(Labs_FilePath, 'r') + labs_data = json.load(f) + labs_collection.insert_one(labs_data) + f.close() + + logging.info('Labs data is dumped into MongoDB. The collection name is - labs_json') + return (labs_collection, labs_data) + +def dump_meds_json_data(): + meds_collection = db.meds_json + for file in os.listdir(Meds_FolderPath): + if file.endswith('.json'): + Meds_FilePath = Meds_FolderPath+file + + f = open(Meds_FilePath, 'r') + meds_data = json.load(f) + meds_collection.insert_one(meds_data) + f.close() + + logging.info('Meds data is dumped into MongoDB. The collection name is - meds_json') + return (meds_collection, meds_data) + +def dump_orders_json_data(): + orders_collection = db.orders_json + for file in os.listdir(Orders_FolderPath): + if file.endswith('.json'): + Orders_FilePath = Orders_FolderPath+file + + f = open(Orders_FilePath, 'r') + orders_data = json.load(f) + orders_collection.insert_one(orders_data) + f.close() + + logging.info('Orders data is dumped into MongoDB. The collection name is - orders_json') + return (orders_collection, orders_data) + +def run_threaded(job_func): + job_thread = threading.Thread(target=job_func) + job_thread.start() + +schedule.every(Labs_ExtractionFrequency).minutes.do(run_threaded, dump_labs_json_data) +schedule.every(Meds_ExtractionFrequency).minutes.do(run_threaded, dump_meds_json_data) +schedule.every(Orders_ExtractionFrequency).minutes.do(run_threaded, dump_orders_json_data) From 27361391d8813dbd116a10b357db45d455bcb619 Mon Sep 17 00:00:00 2001 From: Pradeeban Kathiravelu Date: Tue, 22 Jun 2021 12:03:49 -0400 Subject: [PATCH 29/46] Update intermediate.csv_sample --- .../src/main/resources/intermediary/intermediate.csv_sample | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/app-layer/src/main/resources/intermediary/intermediate.csv_sample b/modules/app-layer/src/main/resources/intermediary/intermediate.csv_sample index c33f644..f19afd0 100644 --- a/modules/app-layer/src/main/resources/intermediary/intermediate.csv_sample +++ b/modules/app-layer/src/main/resources/intermediary/intermediate.csv_sample @@ -1,2 +1,4 @@ DeviceSerialNumber, StudyInstanceUID, PatientID, DurationInMinutes, Number of Series in the Study, Exam Start Time, Exam End Time, StudyDescription -142635, 1.2.345.65454, 123456, 5.433333333, 8, 101744, 102310, StudyDesc \ No newline at end of file +142635, 1.2.345.65454, 123456, 5.433333333, 8, 101744, 102310, StudyDesc +135, 1.3.345.65454, 12365, 5.43333, 2, 34343, 102310, StudyDesc1 +1635, 1.4.345.65454, 1256, 5.433333, 3, 434343, 102310, StudyDec2 From 114f318cbf72d532e49aa2a87baf5624cce4d07b Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Mon, 28 Jun 2021 18:48:53 +0530 Subject: [PATCH 30/46] Issue with png-extraction cmd run --- modules/png-extraction/ImageExtractor.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/png-extraction/ImageExtractor.py b/modules/png-extraction/ImageExtractor.py index 2f90ed3..c13e944 100644 --- a/modules/png-extraction/ImageExtractor.py +++ b/modules/png-extraction/ImageExtractor.py @@ -363,11 +363,7 @@ def Execute(pickle_file, dicom_home, output_directory, print_images, print_only_ total = len(chunk) stamp = time.time() for i in range(len(filedata)): - res = extract_images(filedata, i, png_destination, flattened_to_level, failed, is16Bit) - # (fmap,fail_path,err) = out - fmap = res[1] - fail_path = res[0] - err = res[-1] + (fmap,fail_path,err) = extract_images(filedata, i, png_destination, flattened_to_level, failed, is16Bit) if err: count +=1 copyfile(fail_path[0],fail_path[1]) From 7f2e1213430c64a5d1d70b0698c7f1c97d74f4f7 Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Wed, 30 Jun 2021 18:41:54 +0530 Subject: [PATCH 31/46] Cold-extraction --- modules/cold-extraction/ColdDataRetriever.py | 386 +++++++++++------- modules/frontend/server.py | 105 +++-- .../frontend/templates/cold_extraction.html | 125 +++++- modules/frontend/templates/pngHome.html | 11 +- 4 files changed, 442 insertions(+), 185 deletions(-) diff --git a/modules/cold-extraction/ColdDataRetriever.py b/modules/cold-extraction/ColdDataRetriever.py index bdb2a4c..69fd7ef 100644 --- a/modules/cold-extraction/ColdDataRetriever.py +++ b/modules/cold-extraction/ColdDataRetriever.py @@ -15,109 +15,89 @@ from collections import defaultdict -config = defaultdict(lambda: None) -# Read Default config.json file -with open('config.json', 'r') as f: - tmp_config = json.load(f) - config.update(tmp_config) - -# CLI Argument Parser -ap = argparse.ArgumentParser() - -ap.add_argument("--NifflerSystem", default=config['NifflerSystem'], - help="Path to json file with Niffler System Information.") -ap.add_argument("--StorageFolder", - default=config['StorageFolder'], help="StoreSCP config: Storage Folder. Refer Readme.md") -ap.add_argument("--FilePath", default=config['FilePath'], - help="StoreSCP config: FilePath, Refer configuring config.json in Readme.md.") -ap.add_argument("--CsvFile", default=config['CsvFile'], - help="Path to CSV file for extraction. Refer Readme.md.") -ap.add_argument("--ExtractionType", default=config['ExtractionType'], - help="One of the supported extraction type for Cold Data extraction. Refer Readme.md.") -ap.add_argument("--AccessionIndex", default=config['AccessionIndex'], type=int, - help="Set the CSV column index of AccessionNumber for extractions with Accessions.") -ap.add_argument("--PatientIndex", default=config['PatientIndex'], type=int, - help="Set the CSV column index of EMPI for extractions with EMPI and an accession or EMPI and a date.") -ap.add_argument("--DateIndex", default=config['DateIndex'], type=int, - help="Set the CSV column index of Date(StudyDate, AcquisitionDate) for extractions with EMPI and a date.") -ap.add_argument("--DateType", default=config['DateType'], - help="DateType can range from AcquisitionDate, StudyDate, etc. Refer Readme.md.") -ap.add_argument("--DateFormat", default=config['DateFormat'], - help="DateFormat can range from %Y%m%d, %m/%d/%y, %m-%d-%y, %%m%d%y, etc. Refer Readme.md.") -ap.add_argument("--SendEmail", default=config['SendEmail'], type=bool, - help="Send email when extraction is complete. Default false") -ap.add_argument("--YourEmail", default=config['YourEmail'], - help="A valid email, if send email is enabled.") - -args = vars(ap.parse_args()) - -#Get variables for StoreScp from config.json. -storage_folder = args['StorageFolder'] -file_path = args['FilePath'] - -# Get variables for the each on-demand extraction from config.json -csv_file = args['CsvFile'] -extraction_type = args['ExtractionType'] -accession_index = args['AccessionIndex'] -patient_index = args['PatientIndex'] -date_index = args['DateIndex'] -date_type = args['DateType'] -date_format = args['DateFormat'] -email = args['YourEmail'] -send_email = args['SendEmail'] - -# Reads the system_json file. -system_json = args['NifflerSystem'] - -with open(system_json, 'r') as f: - niffler = json.load(f) - -# Get constants from system.json -DCM4CHE_BIN = niffler['DCM4CHEBin'] -SRC_AET = niffler['SrcAet'] -QUERY_AET = niffler['QueryAet'] -DEST_AET = niffler['DestAet'] -NIGHTLY_ONLY = niffler['NightlyOnly'] -START_HOUR = niffler['StartHour'] -END_HOUR = niffler['EndHour'] -IS_EXTRACTION_NOT_RUNNING = True -NIFFLER_ID = niffler['NifflerID'] -MAX_PROCESSES = niffler['MaxNifflerProcesses'] - -SEPARATOR = ',' - -accessions = [] -patients = [] -dates = [] - -storescp_processes = 0 -niffler_processes = 0 - -nifflerscp_str = "storescp.*{0}".format(QUERY_AET) -qbniffler_str = 'ColdDataRetriever' - -niffler_log = 'niffler' + str(NIFFLER_ID) + '.log' - -logging.basicConfig(filename=niffler_log,level=logging.INFO) -logging.getLogger('schedule').setLevel(logging.WARNING) - -# Variables to track progress between iterations. -extracted_ones = list() - -# By default, assume that this is a fresh extraction. -resume = False - -# All extracted files from the csv file are saved in a respective .pickle file. -try: - with open(csv_file +'.pickle', 'rb') as f: - extracted_ones = pickle.load(f) - # Since we have successfully located a pickle file, it indicates that this is a resume. - resume = True -except: - logging.info("No existing pickle file found. Therefore, initialized with empty value to track the progress to {0}.pickle.".format(csv_file)) - -# record the start time -t_start = time.time() +# MAKING ALL VARIABLES GLOBAL !! +# Add logging.shutdown() upon graceful completion + +# def read_csv(csv_file): +# upload_folder = '../frontend/uploads/csv/' + csv_file # Change the storage folder path +# print("--------- READING CSV FILE ---------") +# with open(upload_folder, newline='') as f: +# reader = csv.reader(f) +# next(f) +# for row in reader: +# print(row) + +def initialize_Values(valuesDict): + global storescp_processes, niffler_processes, nifflerscp_str, qbniffler_str + global storage_folder, file_path, csv_file, extraction_type, accession_index, patient_index, date_index, date_type, date_format, email, send_email + global DCM4CHE_BIN, SRC_AET, QUERY_AET, DEST_AET, NIGHTLY_ONLY, START_HOUR, END_HOUR, IS_EXTRACTION_NOT_RUNNING, NIFFLER_ID, MAX_PROCESSES, SEPARATOR + global accessions, patients, dates, niffler_log, resume + + storage_folder = valuesDict['storage_folder'] + file_path = valuesDict['file_path'] + csv_file = valuesDict['CsvFile'] + extraction_type = valuesDict['extraction_type'] + accession_index = int(valuesDict['accession_index']) + patient_index = int(valuesDict['patient_index']) + date_index = int(valuesDict['date_index']) + date_type = valuesDict['date_type'] + date_format = valuesDict['date_format'] + email = valuesDict['email'] + send_email = bool(valuesDict['send_email']) + + # Reads the system_json file. + # system_json = 'modules/cold-extraction/system.json' + with open('../cold-extraction/system.json', 'r') as f: + niffler = json.load(f) + + # Get constants from system.json + DCM4CHE_BIN = niffler['DCM4CHEBin'] + SRC_AET = niffler['SrcAet'] + QUERY_AET = niffler['QueryAet'] + DEST_AET = niffler['DestAet'] + NIGHTLY_ONLY = niffler['NightlyOnly'] + START_HOUR = niffler['StartHour'] + END_HOUR = niffler['EndHour'] + IS_EXTRACTION_NOT_RUNNING = True + NIFFLER_ID = niffler['NifflerID'] + MAX_PROCESSES = niffler['MaxNifflerProcesses'] + + SEPARATOR = ',' + + accessions = [] + patients = [] + dates = [] + + storescp_processes = 0 + niffler_processes = 0 + + nifflerscp_str = "storescp.*{0}".format(QUERY_AET) + qbniffler_str = 'ColdDataRetriever' + + niffler_log = 'niffler' + str(NIFFLER_ID) + '.log' + + logging.basicConfig(filename=niffler_log,level=logging.INFO) + logging.getLogger('schedule').setLevel(logging.WARNING) + + # Variables to track progress between iterations. + global extracted_ones + extracted_ones = list() + + # By default, assume that this is a fresh extraction. + resume = False + + # All extracted files from the csv file are saved in a respective .pickle file. + try: + with open(csv_file +'.pickle', 'rb') as f: + extracted_ones = pickle.load(f) + # Since we have successfully located a pickle file, it indicates that this is a resume. + resume = True + except: + logging.info("No existing pickle file found. Therefore, initialized with empty value to track the progress to {0}.pickle.".format(csv_file)) + + # record the start time + t_start = time.time() + run_cold_extraction() # Check and kill the StoreScp processes. def check_kill_process(): @@ -141,6 +121,7 @@ def check_kill_process(): def initialize(): global niffler_processes global storescp_processes + print(niffler_processes, storescp_processes, MAX_PROCESSES) for line in os.popen("ps ax | grep " + qbniffler_str + " | grep -v grep"): niffler_processes += 1 for line in os.popen("ps ax | grep -E " + nifflerscp_str + " | grep -v grep"): @@ -150,49 +131,51 @@ def initialize(): if niffler_processes > MAX_PROCESSES: logging.error("[EXTRACTION FAILURE] {0}: Previous {1} extractions still running. As such, your extraction attempt was not successful this time. Please wait until those complete and re-run your query.".format(datetime.datetime.now(), MAX_PROCESSES)) - sys.exit(0) + #sys.exit(0) if storescp_processes >= niffler_processes: logging.info('Killing the idling storescp processes') - check_kill_process() + #check_kill_process() logging.info("{0}: StoreScp process for the current Niffler extraction is starting now".format(datetime.datetime.now())) subprocess.call("{0}/storescp --accept-unknown --directory {1} --filepath {2} -b {3} > storescp.out &".format(DCM4CHE_BIN, storage_folder, file_path, QUERY_AET), shell=True) - -with open(csv_file, newline='') as f: - reader = csv.reader(f) - next(f) - for row in reader: - row = [x.strip() for x in row] - if (extraction_type == 'empi_date'): - if not ((row[patient_index] == "") or (row[date_index] == "")): - patients.append(row[patient_index]) - temp_date = row[date_index] - dt_stamp = datetime.datetime.strptime(temp_date, date_format) - date_str = dt_stamp.strftime('%Y%m%d') - dates.append(date_str) - length = len(patients) - elif (extraction_type == 'empi'): - if not ((row[patient_index] == "")): - patients.append(row[patient_index]) - length = len(patients) - elif (extraction_type == 'accession'): - if not ((row[accession_index] == "")): - accessions.append(row[accession_index]) - length = len(accessions) - elif (extraction_type == 'empi_accession'): - if not ((row[patient_index] == "") or (row[accession_index] == "")): - patients.append(row[patient_index]) - accessions.append(row[accession_index]) - length = len(accessions) +def read_csv(): + upload_folder = '../frontend/uploads/csv/' + csv_file # Change the storage folder path + with open(upload_folder, newline='') as f: + reader = csv.reader(f) + next(f) + for row in reader: + row = [x.strip() for x in row] + if (extraction_type == 'empi_date'): + if not ((row[patient_index] == "") or (row[date_index] == "")): + patients.append(row[patient_index]) + temp_date = row[date_index] + dt_stamp = datetime.datetime.strptime(temp_date, date_format) + date_str = dt_stamp.strftime('%Y%m%d') + dates.append(date_str) + length = len(patients) + elif (extraction_type == 'empi'): + if not ((row[patient_index] == "")): + patients.append(row[patient_index]) + length = len(patients) + elif (extraction_type == 'accession'): + if not ((row[accession_index] == "")): + accessions.append(row[accession_index]) + length = len(accessions) + elif (extraction_type == 'empi_accession'): + if not ((row[patient_index] == "") or (row[accession_index] == "")): + patients.append(row[patient_index]) + accessions.append(row[accession_index]) + length = len(accessions) # Run the retrieval only once, when the extraction script starts, and keep it running in a separate thread. def run_retrieval(): global IS_EXTRACTION_NOT_RUNNING + print("In run_retrieval:", IS_EXTRACTION_NOT_RUNNING) if IS_EXTRACTION_NOT_RUNNING: IS_EXTRACTION_NOT_RUNNING = False logging.info('Starting the extractions...') @@ -294,6 +277,7 @@ def retrieve(): # Write the pickle file periodically to track the progress and persist it to the filesystem def update_pickle(): + print("In update_pickle") global extracted_ones # Pickle using the highest protocol available. with open(csv_file +'.pickle', 'wb') as f: @@ -305,16 +289,130 @@ def run_threaded(job_func): job_thread = threading.Thread(target=job_func) job_thread.start() - -# The thread scheduling -schedule.every(1).minutes.do(run_threaded, run_retrieval) -schedule.every(10).minutes.do(run_threaded, update_pickle) - -# Keep running in a loop. -while True: +def run_cold_extraction(): + print("In run_cold_extraction") + read_csv() + # The thread scheduling + schedule.every(1).minutes.do(run_threaded, run_retrieval) + schedule.every(10).minutes.do(run_threaded, update_pickle) + + # # Keep running in a loop. + while True: + try: + schedule.run_pending() + time.sleep(1) + except KeyboardInterrupt: + check_kill_process() + logging.shutdown() + sys.exit(0) + +if __name__ == "__main__": + global storescp_processes, niffler_processes, nifflerscp_str, qbniffler_str + global storage_folder, file_path, csv_file, extraction_type, accession_index, patient_index, date_index, date_type, date_format, email, send_email + global DCM4CHE_BIN, SRC_AET, QUERY_AET, DEST_AET, NIGHTLY_ONLY, START_HOUR, END_HOUR, IS_EXTRACTION_NOT_RUNNING, NIFFLER_ID, MAX_PROCESSES, SEPARATOR + global accessions, patients, dates, niffler_log, resume + + config = defaultdict(lambda: None) + # Read Default config.json file + with open('config.json', 'r') as f: + tmp_config = json.load(f) + config.update(tmp_config) + + # CLI Argument Parser + ap = argparse.ArgumentParser() + + ap.add_argument("--NifflerSystem", default=config['NifflerSystem'], + help="Path to json file with Niffler System Information.") + ap.add_argument("--StorageFolder", + default=config['StorageFolder'], help="StoreSCP config: Storage Folder. Refer Readme.md") + ap.add_argument("--FilePath", default=config['FilePath'], + help="StoreSCP config: FilePath, Refer configuring config.json in Readme.md.") + ap.add_argument("--CsvFile", default=config['CsvFile'], + help="Path to CSV file for extraction. Refer Readme.md.") + ap.add_argument("--ExtractionType", default=config['ExtractionType'], + help="One of the supported extraction type for Cold Data extraction. Refer Readme.md.") + ap.add_argument("--AccessionIndex", default=config['AccessionIndex'], type=int, + help="Set the CSV column index of AccessionNumber for extractions with Accessions.") + ap.add_argument("--PatientIndex", default=config['PatientIndex'], type=int, + help="Set the CSV column index of EMPI for extractions with EMPI and an accession or EMPI and a date.") + ap.add_argument("--DateIndex", default=config['DateIndex'], type=int, + help="Set the CSV column index of Date(StudyDate, AcquisitionDate) for extractions with EMPI and a date.") + ap.add_argument("--DateType", default=config['DateType'], + help="DateType can range from AcquisitionDate, StudyDate, etc. Refer Readme.md.") + ap.add_argument("--DateFormat", default=config['DateFormat'], + help="DateFormat can range from %Y%m%d, %m/%d/%y, %m-%d-%y, %%m%d%y, etc. Refer Readme.md.") + ap.add_argument("--SendEmail", default=config['SendEmail'], type=bool, + help="Send email when extraction is complete. Default false") + ap.add_argument("--YourEmail", default=config['YourEmail'], + help="A valid email, if send email is enabled.") + + args = vars(ap.parse_args()) + + #Get variables for StoreScp from config.json. + storage_folder = args['StorageFolder'] + file_path = args['FilePath'] + + # Get variables for the each on-demand extraction from config.json + csv_file = args['CsvFile'] + extraction_type = args['ExtractionType'] + accession_index = args['AccessionIndex'] + patient_index = args['PatientIndex'] + date_index = args['DateIndex'] + date_type = args['DateType'] + date_format = args['DateFormat'] + email = args['YourEmail'] + send_email = args['SendEmail'] + + # Reads the system_json file. + system_json = args['NifflerSystem'] + + with open(system_json, 'r') as f: + niffler = json.load(f) + + # Get constants from system.json + DCM4CHE_BIN = niffler['DCM4CHEBin'] + SRC_AET = niffler['SrcAet'] + QUERY_AET = niffler['QueryAet'] + DEST_AET = niffler['DestAet'] + NIGHTLY_ONLY = niffler['NightlyOnly'] + START_HOUR = niffler['StartHour'] + END_HOUR = niffler['EndHour'] + IS_EXTRACTION_NOT_RUNNING = True + NIFFLER_ID = niffler['NifflerID'] + MAX_PROCESSES = niffler['MaxNifflerProcesses'] + + SEPARATOR = ',' + + accessions = [] + patients = [] + dates = [] + + storescp_processes = 0 + niffler_processes = 0 + + nifflerscp_str = "storescp.*{0}".format(QUERY_AET) + qbniffler_str = 'ColdDataRetriever' + + niffler_log = 'niffler' + str(NIFFLER_ID) + '.log' + + logging.basicConfig(filename=niffler_log,level=logging.INFO) + logging.getLogger('schedule').setLevel(logging.WARNING) + + # Variables to track progress between iterations. + extracted_ones = list() + + # By default, assume that this is a fresh extraction. + resume = False + + # All extracted files from the csv file are saved in a respective .pickle file. try: - schedule.run_pending() - time.sleep(1) - except KeyboardInterrupt: - check_kill_process() - sys.exit(0) + with open(csv_file +'.pickle', 'rb') as f: + extracted_ones = pickle.load(f) + # Since we have successfully located a pickle file, it indicates that this is a resume. + resume = True + except: + logging.info("No existing pickle file found. Therefore, initialized with empty value to track the progress to {0}.pickle.".format(csv_file)) + + # record the start time + t_start = time.time() + run_cold_extraction() diff --git a/modules/frontend/server.py b/modules/frontend/server.py index 134b731..0e94ed6 100644 --- a/modules/frontend/server.py +++ b/modules/frontend/server.py @@ -4,6 +4,7 @@ from flask_sqlalchemy import SQLAlchemy from flask_login import UserMixin from flask_login import LoginManager, login_user, login_required, current_user, logout_user +from werkzeug.utils import secure_filename import warnings warnings.filterwarnings("ignore") @@ -12,6 +13,8 @@ from models import User PEOPLE_FOLDER = os.path.join('static','styles') +UPLOAD_FOLDER = './uploads/csv' # Need to change this to a particular server path +app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER # app = Flask(__name__) login_manager = LoginManager(app) @@ -24,7 +27,7 @@ def load_user(user_id): @app.route("/", methods=['GET']) def index(): - return render_template('home.html', isAdmin = isAdmin) + return render_template('Home.html', isAdmin = isAdmin) @app.route('/login', methods=['GET','POST']) def login(): @@ -43,7 +46,7 @@ def login(): # if the above check passes, then we know the user has the right credentials login_user(user, remember=remember) - return render_template('home.html') + return render_template('Home.html') return render_template('login.html', isAdmin = isAdmin) @app.route('/signup', methods=['GET','POST']) @@ -74,31 +77,34 @@ def signup(): @login_required def logout(): logout_user() - return render_template('home.html', isAdmin = isAdmin) - -# @app.route("/png-extraction", methods = ['GET']) -# @login_required -# def PNG_Extraction(): -# return render_template('pngHome.html') - -config_values = {} + return render_template('Home.html', isAdmin = isAdmin) @app.route('/png-extraction', methods=['GET', 'POST']) @login_required def extract_png(): + config_values = {} if request.method =='POST': + depth = request.form['depth'] + if(depth == '' or len(depth) == 0): + depth = '0' + chunks = request.form['chunks'] + if(chunks == '' or len(chunks) == 0): + chunks = '1' + useProcess = request.form['useProcess'] + if(useProcess == '' or len(useProcess) == 0): + useProcess = '0' + config_values["dcmFolder"] = request.form['DICOMFolder'] config_values["outputFolder"] = request.form['outputFolder'] - config_values["depth"] = request.form['depth'] - config_values["chunks"] = request.form['chunks'] - config_values["useProcess"] = request.form['useProcess'] + config_values["depth"] = depth + config_values["chunks"] = chunks + config_values["useProcess"] = useProcess config_values["level"] = request.form['level'] config_values["16Bit"] = request.form['16Bit'] config_values["printImages"] = request.form['printImages'] config_values["headers"] = request.form['headers'] config_values["sendEmail"] = request.form['sendEmail'] config_values["email"] = request.form['email'] - if(len(config_values) > 0): import sys sys.path.append("../png-extraction/") @@ -110,17 +116,68 @@ def extract_png(): @app.route('/cold-extraction', methods=['GET', 'POST']) @login_required def cold_extraction(): - if request.method =='POST': - csv_file = request.files['csvFile'] - if (csv_file): - import sys - import io + + csv_folder = UPLOAD_FOLDER + files_present_in_server = os.listdir(csv_folder) - stream = io.StringIO(csv_file.stream.read().decode("UTF8"), newline=None) - sys.path.append("../cold-extraction/") - import ColdDataRetriever - x = ColdDataRetriever.read_csv(stream) - return render_template('cold_extraction.html') + cold_extraction_values = {} + if request.method =='POST': + # f1 = request.form['csvFile'] + # import pathlib + # p1 = pathlib.PureWindowsPath(f1) + # f2 = p1.as_posix() # csv_file path + # cold_extraction_values['csv_file'] = f2 + + f1 = request.files['csvFile_choose'] + f2 = request.form['csvFile_name'] + if(f1): + filename = secure_filename(f1.filename) + f1.save(os.path.join(app.config['UPLOAD_FOLDER'],filename)) + cold_extraction_values['CsvFile'] = filename + else: + cold_extraction_values['CsvFile'] = f2 + + NifflerSystem = request.form['NifflerSystem'] + if(NifflerSystem == '' or len(NifflerSystem) == 0): + NifflerSystem = 'system.json' + file_path = request.form['file_path'] + if(file_path == '' or len(file_path) == 0): + file_path = '{00100020}/{0020000D}/{0020000E}/{00080018}.dcm' + accession_index = request.form['AccessionIndex'] + if(accession_index == '' or len(accession_index) == 0): + accession_index = '1' + patient_index = request.form['PatientIndex'] + if(patient_index == '' or len(patient_index) == 0): + patient_index = '0' + date_index = request.form['DateIndex'] + if(date_index == '' or len(date_index) == 0): + date_index = '1' + date_format = request.form['DateFormat'] + if(date_format == '' or len(date_format) == 0): + date_format = '%Y%m%d' + + cold_extraction_values['NifflerSystem'] = NifflerSystem + cold_extraction_values['storage_folder'] = request.form['StorageFolder'] + cold_extraction_values['file_path'] = file_path + cold_extraction_values['extraction_type'] = request.form['ExtractionType'] + cold_extraction_values['accession_index'] = accession_index + cold_extraction_values['patient_index'] = patient_index + cold_extraction_values['date_index'] = date_index + cold_extraction_values['date_type'] = request.form['DateType'] + cold_extraction_values['date_format'] = date_format + cold_extraction_values['send_email'] = request.form['sendEmail'] + cold_extraction_values['email'] = request.form['email'] + print(cold_extraction_values) + + import sys + import io + # stream = io.StringIO(csv_file.stream.read().decode("UTF8"), newline=None) + sys.path.append("../cold-extraction/") + import ColdDataRetriever + x = ColdDataRetriever.initialize_Values(cold_extraction_values) + return render_template('cold_extraction.html') + # x = ColdDataRetriever.read_csv(cold_extraction_values) + return render_template('cold_extraction.html', files_list = files_present_in_server) #JUST DO IT!!! if __name__=="__main__": app.run(port="9000") \ No newline at end of file diff --git a/modules/frontend/templates/cold_extraction.html b/modules/frontend/templates/cold_extraction.html index aae7df6..6dc5930 100644 --- a/modules/frontend/templates/cold_extraction.html +++ b/modules/frontend/templates/cold_extraction.html @@ -35,6 +35,36 @@

Edit Configurations

+ +
+
+ +
+
+
Niffler System*
+
+
+ +
+
+
-
Storage Folder
+
Storage Folder*
-
-
-
CSV File
+ +
-
+
-->
-
File Path*
+
File Path
type="text" name="file_path" value="{00100020}/{0020000D}/{0020000E}/{00080018}.dcm" + style="font-size: 15px" />
@@ -338,11 +388,12 @@

aria-label="Default select example" name="sendEmail" onchange="yesnoCheck(this);" - required > - + - +

@@ -373,7 +424,42 @@

/>

- +
+
+ +
+
+
CSV File*
+
+
+ +
+

OR

+
+ +
+
@@ -405,7 +491,7 @@

padding: 25px; " > -
+ +
+

+ FILES PRESENT ON SERVER +

+
+
+ {% if files_list %} {% for files in files_list %} +

→ {{ files }}

+ {% endfor %} {% else %} +

+ No files present on server currently !!
+ Please upload files from your system +

{% endif %} +

diff --git a/modules/frontend/templates/pngHome.html b/modules/frontend/templates/pngHome.html index cd3fc19..8dd5197 100644 --- a/modules/frontend/templates/pngHome.html +++ b/modules/frontend/templates/pngHome.html @@ -50,7 +50,7 @@

-
DICOM Home Folder
+
DICOM Home Folder*
@@ -87,7 +87,7 @@

-
Output Folder
+
Output Folder*
- +
From 5feeff613fd6d7996417715242930a4bcc6e1d09 Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Wed, 30 Jun 2021 13:48:50 +0000 Subject: [PATCH 32/46] Added access for public --- modules/frontend/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/frontend/server.py b/modules/frontend/server.py index 0e94ed6..a4116b8 100644 --- a/modules/frontend/server.py +++ b/modules/frontend/server.py @@ -180,4 +180,4 @@ def cold_extraction(): return render_template('cold_extraction.html', files_list = files_present_in_server) #JUST DO IT!!! if __name__=="__main__": - app.run(port="9000") \ No newline at end of file + app.run(host="0.0.0.0",port="9000") From 026e33142c9373ea46c7b28b9dfc112e5230ea77 Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Wed, 30 Jun 2021 19:57:35 +0530 Subject: [PATCH 33/46] Updated cold-extraction --- modules/cold-extraction/ColdDataRetriever.py | 36 ++++++++------------ modules/frontend/server.py | 14 ++------ 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/modules/cold-extraction/ColdDataRetriever.py b/modules/cold-extraction/ColdDataRetriever.py index 69fd7ef..4756d3a 100644 --- a/modules/cold-extraction/ColdDataRetriever.py +++ b/modules/cold-extraction/ColdDataRetriever.py @@ -15,24 +15,15 @@ from collections import defaultdict -# MAKING ALL VARIABLES GLOBAL !! -# Add logging.shutdown() upon graceful completion - -# def read_csv(csv_file): -# upload_folder = '../frontend/uploads/csv/' + csv_file # Change the storage folder path -# print("--------- READING CSV FILE ---------") -# with open(upload_folder, newline='') as f: -# reader = csv.reader(f) -# next(f) -# for row in reader: -# print(row) - def initialize_Values(valuesDict): global storescp_processes, niffler_processes, nifflerscp_str, qbniffler_str global storage_folder, file_path, csv_file, extraction_type, accession_index, patient_index, date_index, date_type, date_format, email, send_email global DCM4CHE_BIN, SRC_AET, QUERY_AET, DEST_AET, NIGHTLY_ONLY, START_HOUR, END_HOUR, IS_EXTRACTION_NOT_RUNNING, NIFFLER_ID, MAX_PROCESSES, SEPARATOR - global accessions, patients, dates, niffler_log, resume + global accessions, patients, dates, niffler_log, resume, upload_folder + upload_folder = './csv/' + if not os.path.exists(upload_folder): + os.makedirs(upload_folder) storage_folder = valuesDict['storage_folder'] file_path = valuesDict['file_path'] csv_file = valuesDict['CsvFile'] @@ -121,7 +112,6 @@ def check_kill_process(): def initialize(): global niffler_processes global storescp_processes - print(niffler_processes, storescp_processes, MAX_PROCESSES) for line in os.popen("ps ax | grep " + qbniffler_str + " | grep -v grep"): niffler_processes += 1 for line in os.popen("ps ax | grep -E " + nifflerscp_str + " | grep -v grep"): @@ -131,11 +121,11 @@ def initialize(): if niffler_processes > MAX_PROCESSES: logging.error("[EXTRACTION FAILURE] {0}: Previous {1} extractions still running. As such, your extraction attempt was not successful this time. Please wait until those complete and re-run your query.".format(datetime.datetime.now(), MAX_PROCESSES)) - #sys.exit(0) + sys.exit(0) if storescp_processes >= niffler_processes: logging.info('Killing the idling storescp processes') - #check_kill_process() + check_kill_process() logging.info("{0}: StoreScp process for the current Niffler extraction is starting now".format(datetime.datetime.now())) @@ -143,8 +133,9 @@ def initialize(): def read_csv(): - upload_folder = '../frontend/uploads/csv/' + csv_file # Change the storage folder path - with open(upload_folder, newline='') as f: + global upload_folder + upload_file = upload_folder + csv_file # Change the storage folder path + with open(upload_file, newline='') as f: reader = csv.reader(f) next(f) for row in reader: @@ -175,7 +166,6 @@ def read_csv(): # Run the retrieval only once, when the extraction script starts, and keep it running in a separate thread. def run_retrieval(): global IS_EXTRACTION_NOT_RUNNING - print("In run_retrieval:", IS_EXTRACTION_NOT_RUNNING) if IS_EXTRACTION_NOT_RUNNING: IS_EXTRACTION_NOT_RUNNING = False logging.info('Starting the extractions...') @@ -277,7 +267,6 @@ def retrieve(): # Write the pickle file periodically to track the progress and persist it to the filesystem def update_pickle(): - print("In update_pickle") global extracted_ones # Pickle using the highest protocol available. with open(csv_file +'.pickle', 'wb') as f: @@ -290,7 +279,6 @@ def run_threaded(job_func): job_thread.start() def run_cold_extraction(): - print("In run_cold_extraction") read_csv() # The thread scheduling schedule.every(1).minutes.do(run_threaded, run_retrieval) @@ -310,7 +298,11 @@ def run_cold_extraction(): global storescp_processes, niffler_processes, nifflerscp_str, qbniffler_str global storage_folder, file_path, csv_file, extraction_type, accession_index, patient_index, date_index, date_type, date_format, email, send_email global DCM4CHE_BIN, SRC_AET, QUERY_AET, DEST_AET, NIGHTLY_ONLY, START_HOUR, END_HOUR, IS_EXTRACTION_NOT_RUNNING, NIFFLER_ID, MAX_PROCESSES, SEPARATOR - global accessions, patients, dates, niffler_log, resume + global accessions, patients, dates, niffler_log, resume, upload_folder + + upload_folder = './csv/' + if not os.path.exists(upload_folder): + os.makedirs(upload_folder) config = defaultdict(lambda: None) # Read Default config.json file diff --git a/modules/frontend/server.py b/modules/frontend/server.py index a4116b8..656c787 100644 --- a/modules/frontend/server.py +++ b/modules/frontend/server.py @@ -13,7 +13,7 @@ from models import User PEOPLE_FOLDER = os.path.join('static','styles') -UPLOAD_FOLDER = './uploads/csv' # Need to change this to a particular server path +UPLOAD_FOLDER = '../cold-extraction/csv' # Need to change this to a particular server path app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER # app = Flask(__name__) @@ -116,18 +116,13 @@ def extract_png(): @app.route('/cold-extraction', methods=['GET', 'POST']) @login_required def cold_extraction(): - csv_folder = UPLOAD_FOLDER + if not os.path.exists(csv_folder): + os.makedirs(csv_folder) files_present_in_server = os.listdir(csv_folder) cold_extraction_values = {} if request.method =='POST': - # f1 = request.form['csvFile'] - # import pathlib - # p1 = pathlib.PureWindowsPath(f1) - # f2 = p1.as_posix() # csv_file path - # cold_extraction_values['csv_file'] = f2 - f1 = request.files['csvFile_choose'] f2 = request.form['csvFile_name'] if(f1): @@ -167,16 +162,13 @@ def cold_extraction(): cold_extraction_values['date_format'] = date_format cold_extraction_values['send_email'] = request.form['sendEmail'] cold_extraction_values['email'] = request.form['email'] - print(cold_extraction_values) import sys import io - # stream = io.StringIO(csv_file.stream.read().decode("UTF8"), newline=None) sys.path.append("../cold-extraction/") import ColdDataRetriever x = ColdDataRetriever.initialize_Values(cold_extraction_values) return render_template('cold_extraction.html') - # x = ColdDataRetriever.read_csv(cold_extraction_values) return render_template('cold_extraction.html', files_list = files_present_in_server) #JUST DO IT!!! if __name__=="__main__": From f2e70b5a9d67f81f1403f16061542faf193a313a Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Thu, 1 Jul 2021 18:22:08 +0530 Subject: [PATCH 34/46] Requested Changes applied --- modules/cold-extraction/ColdDataRetriever.py | 33 +++++++++++-------- modules/frontend/server.py | 2 +- .../frontend/templates/cold_extraction.html | 7 ++++ 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/modules/cold-extraction/ColdDataRetriever.py b/modules/cold-extraction/ColdDataRetriever.py index 4756d3a..04794ca 100644 --- a/modules/cold-extraction/ColdDataRetriever.py +++ b/modules/cold-extraction/ColdDataRetriever.py @@ -17,11 +17,13 @@ def initialize_Values(valuesDict): global storescp_processes, niffler_processes, nifflerscp_str, qbniffler_str - global storage_folder, file_path, csv_file, extraction_type, accession_index, patient_index, date_index, date_type, date_format, email, send_email + global storage_folder, file_path, csv_file, extraction_type, accession_index, patient_index, date_index, date_type, date_format, email, send_email, system_json global DCM4CHE_BIN, SRC_AET, QUERY_AET, DEST_AET, NIGHTLY_ONLY, START_HOUR, END_HOUR, IS_EXTRACTION_NOT_RUNNING, NIFFLER_ID, MAX_PROCESSES, SEPARATOR global accessions, patients, dates, niffler_log, resume, upload_folder - upload_folder = './csv/' + logs = [] # For showing logs on frontend if any error occurs + + upload_folder = '../cold-extraction/csv/' # This is a check for frontend only,it won't conflict when program is run from terminal if not os.path.exists(upload_folder): os.makedirs(upload_folder) storage_folder = valuesDict['storage_folder'] @@ -35,11 +37,21 @@ def initialize_Values(valuesDict): date_format = valuesDict['date_format'] email = valuesDict['email'] send_email = bool(valuesDict['send_email']) + system_json = valuesDict['NifflerSystem'] + + csv_file = upload_folder + csv_file # Reads the system_json file. - # system_json = 'modules/cold-extraction/system.json' - with open('../cold-extraction/system.json', 'r') as f: - niffler = json.load(f) + # This is a check as if user enters wrong json then the frontend would notify user + system_json_file = '../cold-extraction/' + system_json + try: + with open(system_json_file, 'r') as f: + niffler = json.load(f) + except: + err = "Error could not load given " + system_json + " file !!" + logs.append(err) + logging.shutdown() + return logs # Get constants from system.json DCM4CHE_BIN = niffler['DCM4CHEBin'] @@ -89,6 +101,7 @@ def initialize_Values(valuesDict): # record the start time t_start = time.time() run_cold_extraction() + return logs # Check and kill the StoreScp processes. def check_kill_process(): @@ -133,9 +146,7 @@ def initialize(): def read_csv(): - global upload_folder - upload_file = upload_folder + csv_file # Change the storage folder path - with open(upload_file, newline='') as f: + with open(csv_file, newline='') as f: reader = csv.reader(f) next(f) for row in reader: @@ -298,11 +309,7 @@ def run_cold_extraction(): global storescp_processes, niffler_processes, nifflerscp_str, qbniffler_str global storage_folder, file_path, csv_file, extraction_type, accession_index, patient_index, date_index, date_type, date_format, email, send_email global DCM4CHE_BIN, SRC_AET, QUERY_AET, DEST_AET, NIGHTLY_ONLY, START_HOUR, END_HOUR, IS_EXTRACTION_NOT_RUNNING, NIFFLER_ID, MAX_PROCESSES, SEPARATOR - global accessions, patients, dates, niffler_log, resume, upload_folder - - upload_folder = './csv/' - if not os.path.exists(upload_folder): - os.makedirs(upload_folder) + global accessions, patients, dates, niffler_log, resume config = defaultdict(lambda: None) # Read Default config.json file diff --git a/modules/frontend/server.py b/modules/frontend/server.py index 656c787..0076f45 100644 --- a/modules/frontend/server.py +++ b/modules/frontend/server.py @@ -168,7 +168,7 @@ def cold_extraction(): sys.path.append("../cold-extraction/") import ColdDataRetriever x = ColdDataRetriever.initialize_Values(cold_extraction_values) - return render_template('cold_extraction.html') + return render_template('cold_extraction.html', logs = x, files_list = files_present_in_server) return render_template('cold_extraction.html', files_list = files_present_in_server) #JUST DO IT!!! if __name__=="__main__": diff --git a/modules/frontend/templates/cold_extraction.html b/modules/frontend/templates/cold_extraction.html index 6dc5930..dc1e479 100644 --- a/modules/frontend/templates/cold_extraction.html +++ b/modules/frontend/templates/cold_extraction.html @@ -521,6 +521,13 @@

{% endif %}
+ {% if logs %} +

Errors: {{ logs[0] }}

+ {% else %} +

+ Run On-demand Extraction to check Logs +

+ {% endif %}
From 29ec9bbfb83ec04d35f1173dd885a5be0073f756 Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Thu, 1 Jul 2021 19:34:55 +0530 Subject: [PATCH 35/46] Changes made --- modules/cold-extraction/ColdDataRetriever.py | 24 ++------- modules/frontend/server.py | 57 +++++++++++++------- 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/modules/cold-extraction/ColdDataRetriever.py b/modules/cold-extraction/ColdDataRetriever.py index 04794ca..4faaf23 100644 --- a/modules/cold-extraction/ColdDataRetriever.py +++ b/modules/cold-extraction/ColdDataRetriever.py @@ -19,13 +19,8 @@ def initialize_Values(valuesDict): global storescp_processes, niffler_processes, nifflerscp_str, qbniffler_str global storage_folder, file_path, csv_file, extraction_type, accession_index, patient_index, date_index, date_type, date_format, email, send_email, system_json global DCM4CHE_BIN, SRC_AET, QUERY_AET, DEST_AET, NIGHTLY_ONLY, START_HOUR, END_HOUR, IS_EXTRACTION_NOT_RUNNING, NIFFLER_ID, MAX_PROCESSES, SEPARATOR - global accessions, patients, dates, niffler_log, resume, upload_folder - - logs = [] # For showing logs on frontend if any error occurs + global accessions, patients, dates, niffler_log, resume - upload_folder = '../cold-extraction/csv/' # This is a check for frontend only,it won't conflict when program is run from terminal - if not os.path.exists(upload_folder): - os.makedirs(upload_folder) storage_folder = valuesDict['storage_folder'] file_path = valuesDict['file_path'] csv_file = valuesDict['CsvFile'] @@ -38,21 +33,11 @@ def initialize_Values(valuesDict): email = valuesDict['email'] send_email = bool(valuesDict['send_email']) system_json = valuesDict['NifflerSystem'] - - csv_file = upload_folder + csv_file # Reads the system_json file. - # This is a check as if user enters wrong json then the frontend would notify user - system_json_file = '../cold-extraction/' + system_json - try: - with open(system_json_file, 'r') as f: - niffler = json.load(f) - except: - err = "Error could not load given " + system_json + " file !!" - logs.append(err) - logging.shutdown() - return logs - + with open(system_json, 'r') as f: + niffler = json.load(f) + # Get constants from system.json DCM4CHE_BIN = niffler['DCM4CHEBin'] SRC_AET = niffler['SrcAet'] @@ -101,7 +86,6 @@ def initialize_Values(valuesDict): # record the start time t_start = time.time() run_cold_extraction() - return logs # Check and kill the StoreScp processes. def check_kill_process(): diff --git a/modules/frontend/server.py b/modules/frontend/server.py index 0076f45..220d58f 100644 --- a/modules/frontend/server.py +++ b/modules/frontend/server.py @@ -15,6 +15,9 @@ PEOPLE_FOLDER = os.path.join('static','styles') UPLOAD_FOLDER = '../cold-extraction/csv' # Need to change this to a particular server path app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER + +COLD_UPLOAD_FOLDER = '../cold-extraction/' # Need to change this to a particular server path +app.config['COLD_UPLOAD_FOLDER'] = COLD_UPLOAD_FOLDER # app = Flask(__name__) login_manager = LoginManager(app) @@ -116,6 +119,7 @@ def extract_png(): @app.route('/cold-extraction', methods=['GET', 'POST']) @login_required def cold_extraction(): + logs = [] csv_folder = UPLOAD_FOLDER if not os.path.exists(csv_folder): os.makedirs(csv_folder) @@ -128,9 +132,9 @@ def cold_extraction(): if(f1): filename = secure_filename(f1.filename) f1.save(os.path.join(app.config['UPLOAD_FOLDER'],filename)) - cold_extraction_values['CsvFile'] = filename + cold_extraction_values['CsvFile'] = os.path.join(app.config['UPLOAD_FOLDER'],filename) else: - cold_extraction_values['CsvFile'] = f2 + cold_extraction_values['CsvFile'] = os.path.join(app.config['UPLOAD_FOLDER'],f2) NifflerSystem = request.form['NifflerSystem'] if(NifflerSystem == '' or len(NifflerSystem) == 0): @@ -151,24 +155,37 @@ def cold_extraction(): if(date_format == '' or len(date_format) == 0): date_format = '%Y%m%d' - cold_extraction_values['NifflerSystem'] = NifflerSystem - cold_extraction_values['storage_folder'] = request.form['StorageFolder'] - cold_extraction_values['file_path'] = file_path - cold_extraction_values['extraction_type'] = request.form['ExtractionType'] - cold_extraction_values['accession_index'] = accession_index - cold_extraction_values['patient_index'] = patient_index - cold_extraction_values['date_index'] = date_index - cold_extraction_values['date_type'] = request.form['DateType'] - cold_extraction_values['date_format'] = date_format - cold_extraction_values['send_email'] = request.form['sendEmail'] - cold_extraction_values['email'] = request.form['email'] - - import sys - import io - sys.path.append("../cold-extraction/") - import ColdDataRetriever - x = ColdDataRetriever.initialize_Values(cold_extraction_values) - return render_template('cold_extraction.html', logs = x, files_list = files_present_in_server) + NifflerSystem_File = COLD_UPLOAD_FOLDER + NifflerSystem + checkfile = True + try: + with open(NifflerSystem_File, 'r') as f: + checkfile = True + except: + err = "Error could not load given " + NifflerSystem + " file !!" + logs.append(err) + checkfile = False + + if checkfile: + cold_extraction_values['NifflerSystem'] = NifflerSystem_File + cold_extraction_values['storage_folder'] = request.form['StorageFolder'] + cold_extraction_values['file_path'] = file_path + cold_extraction_values['extraction_type'] = request.form['ExtractionType'] + cold_extraction_values['accession_index'] = accession_index + cold_extraction_values['patient_index'] = patient_index + cold_extraction_values['date_index'] = date_index + cold_extraction_values['date_type'] = request.form['DateType'] + cold_extraction_values['date_format'] = date_format + cold_extraction_values['send_email'] = request.form['sendEmail'] + cold_extraction_values['email'] = request.form['email'] + + import sys + import io + sys.path.append("../cold-extraction/") + import ColdDataRetriever + x = ColdDataRetriever.initialize_Values(cold_extraction_values) + return render_template('cold_extraction.html', logs = logs, files_list = files_present_in_server) + else: + return render_template('cold_extraction.html', logs = logs, files_list = files_present_in_server) return render_template('cold_extraction.html', files_list = files_present_in_server) #JUST DO IT!!! if __name__=="__main__": From 7b2238bd384bfa3e8f2afcac932d06e8efaa33d0 Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Thu, 1 Jul 2021 22:07:21 +0530 Subject: [PATCH 36/46] Changes --- modules/cold-extraction/ColdDataRetriever.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/cold-extraction/ColdDataRetriever.py b/modules/cold-extraction/ColdDataRetriever.py index 4faaf23..0edd43f 100644 --- a/modules/cold-extraction/ColdDataRetriever.py +++ b/modules/cold-extraction/ColdDataRetriever.py @@ -19,7 +19,7 @@ def initialize_Values(valuesDict): global storescp_processes, niffler_processes, nifflerscp_str, qbniffler_str global storage_folder, file_path, csv_file, extraction_type, accession_index, patient_index, date_index, date_type, date_format, email, send_email, system_json global DCM4CHE_BIN, SRC_AET, QUERY_AET, DEST_AET, NIGHTLY_ONLY, START_HOUR, END_HOUR, IS_EXTRACTION_NOT_RUNNING, NIFFLER_ID, MAX_PROCESSES, SEPARATOR - global accessions, patients, dates, niffler_log, resume + global accessions, patients, dates, niffler_log, resume, length storage_folder = valuesDict['storage_folder'] file_path = valuesDict['file_path'] @@ -130,6 +130,7 @@ def initialize(): def read_csv(): + global length with open(csv_file, newline='') as f: reader = csv.reader(f) next(f) @@ -170,6 +171,7 @@ def run_retrieval(): # The core DICOM on-demand retrieve process. def retrieve(): + global length # For the cases that have the typical EMPI and Accession values together. if (extraction_type == 'empi_accession'): # Create our Identifier (query) dataset @@ -293,7 +295,7 @@ def run_cold_extraction(): global storescp_processes, niffler_processes, nifflerscp_str, qbniffler_str global storage_folder, file_path, csv_file, extraction_type, accession_index, patient_index, date_index, date_type, date_format, email, send_email global DCM4CHE_BIN, SRC_AET, QUERY_AET, DEST_AET, NIGHTLY_ONLY, START_HOUR, END_HOUR, IS_EXTRACTION_NOT_RUNNING, NIFFLER_ID, MAX_PROCESSES, SEPARATOR - global accessions, patients, dates, niffler_log, resume + global accessions, patients, dates, niffler_log, resume, length config = defaultdict(lambda: None) # Read Default config.json file From 2827aff4ed6b8b28e58207fc7f2dd83f31959369 Mon Sep 17 00:00:00 2001 From: Chinmay Vibhute Date: Thu, 1 Jul 2021 23:05:42 +0530 Subject: [PATCH 37/46] refactor meta extractor for unit tests --- modules/meta-extraction/MetadataExtractor.py | 180 +++++++++++-------- 1 file changed, 104 insertions(+), 76 deletions(-) diff --git a/modules/meta-extraction/MetadataExtractor.py b/modules/meta-extraction/MetadataExtractor.py index 9afb39b..8c98d3e 100644 --- a/modules/meta-extraction/MetadataExtractor.py +++ b/modules/meta-extraction/MetadataExtractor.py @@ -28,59 +28,22 @@ import json -with open('service/system.json', 'r') as f: - niffler = json.load(f) - -# Get constants from system.json -DCM4CHE_BIN = niffler['DCM4CHEBin'] -STORAGE_FOLDER = niffler['StorageFolder'] -FILE_PATH = niffler['FilePath'] -QUERY_AET = niffler['QueryAet'] - -# Global Constants: Configurations and folder locations -EXTRACTION_RUNNING = False -IS_DCM4CHE_NOT_RUNNING = True -logging.basicConfig(level=logging.INFO) - -FEATURES_FOLDER = os.getcwd() + "/conf/" -PICKLE_FOLDER = os.getcwd() + "/pickles/" - - -# Variables to track progress between iterations. -processed_series_but_yet_to_delete = list() -processed_and_deleted_series = list() - - -# Get features of a txt file -features_lists = list() -feature_files = list() - -# All processed series is saved between iterations as pickle files. -try: - with open(PICKLE_FOLDER + 'processed_series_but_yet_to_delete.pickle', 'rb') as f: - processed_series_but_yet_to_delete = pickle.load(f) -except: - logging.info("Unable to load a valid pickle file. Initialized with empty value for processed_series_but_yet_to_delete") - -try: - with open(PICKLE_FOLDER + 'processed_and_deleted_series.pickle', 'rb') as f: - processed_and_deleted_series = pickle.load(f) -except: - logging.info("Unable to load a valid pickle file. Initialized with empty value for processed_and_deleted_series") - -# Read the txt file which includes the features, then extract them -os.chdir(FEATURES_FOLDER) -txt_files = glob.glob('*.txt') -logging.info('Number of files consisting of the features to extract: %s', str(len(txt_files))) - -for file in txt_files: - filename, file_extension = os.path.splitext(file) - text_file = open(file, "r") - feature_list = text_file.read().split('\n') - del feature_list[-1] - features_lists.append(feature_list) - feature_files.append(filename) - +# Define Global Vars +# (Initialization outside of __main__ needed for mocking in unit tests) +# These are overwritten in __main__ +DCM4CHE_BIN = None +STORAGE_FOLDER = None +FILE_PATH = None +QUERY_AET = None +EXTRACTION_RUNNING = None +IS_DCM4CHE_NOT_RUNNING = None +FEATURES_FOLDER = None +PICKLE_FOLDER = None +DB = None +processed_series_but_yet_to_delete = None +processed_and_deleted_series = None +features_lists = None +feature_files = None # Function for getting tuple for field, val pairs for this file # plan is instance of dicom class, the data for single mammo file @@ -133,6 +96,7 @@ def get_dict_fields(bigdict, features): # The core method of extracting metadata def extract(): + global STORAGE_FOLDER os.chdir(STORAGE_FOLDER) if len([name for name in os.listdir(".") if os.path.isdir(name)]) == 0: # Print once if the storage folder is empty. @@ -156,6 +120,7 @@ def extract_metadata(): global EXTRACTION_RUNNING global features_lists global feature_files + global DB extracted_in_this_iteration = 0 first_inst_of_series = list() @@ -221,10 +186,11 @@ def extract_metadata(): # Delete the processed DICOM objects from the storage. def clear_storage(): - os.chdir(STORAGE_FOLDER) + global STORAGE_FOLDER global processed_series_but_yet_to_delete global processed_and_deleted_series + os.chdir(STORAGE_FOLDER) deleted_in_this_iteration = 0 logging.info('Clean up process initiated at series level at: %s', str(datetime.datetime.now())) @@ -260,7 +226,8 @@ def clear_storage(): def update_pickle(): global processed_series_but_yet_to_delete global processed_and_deleted_series - + global PICKLE_FOLDER + # Pickle using the highest protocol available. with open(PICKLE_FOLDER + 'processed_series_but_yet_to_delete.pickle', 'wb') as f: pickle.dump(processed_series_but_yet_to_delete, f, pickle.HIGHEST_PROTOCOL) @@ -272,6 +239,7 @@ def update_pickle(): # Measure storage folder disk space utilization def measure_diskutil(): + global STORAGE_FOLDER total, used, free = shutil.disk_usage(STORAGE_FOLDER) logging.info("Disk space used by the Storage Folder: %d GB" % (used // (2**30))) @@ -279,6 +247,10 @@ def measure_diskutil(): # Run DCM4CHE only once, when the extraction script starts, and keep it running in a separate thread. def run_dcm4che(): global IS_DCM4CHE_NOT_RUNNING + global STORAGE_FOLDER + global FILE_PATH + global QUERY_AET + if IS_DCM4CHE_NOT_RUNNING: IS_DCM4CHE_NOT_RUNNING = False logging.info('Starting DCM4CHE..') @@ -291,31 +263,87 @@ def run_threaded(job_func): job_thread.start() -logging.info('The execution started at: %s', str(datetime.datetime.now())) +if __name__ == "__main__": -logging.debug('Debug logs enabled.') + with open('service/system.json', 'r') as f: + niffler = json.load(f) + + # Set Niffler Config. + DCM4CHE_BIN = niffler['DCM4CHEBin'] + STORAGE_FOLDER = niffler['StorageFolder'] + FILE_PATH = niffler['FilePath'] + QUERY_AET = niffler['QueryAet'] -# Create connections for communicating with Mongo DB server, to store metadata -try: - if os.environ['MONGO_URI'] != "": - mongo_uri = 'mongodb://' + os.environ['MONGO_URI'] - else: + + # Global Constants: Configurations and folder locations + EXTRACTION_RUNNING = False + IS_DCM4CHE_NOT_RUNNING = True + logging.basicConfig(level=logging.INFO) + + FEATURES_FOLDER = os.getcwd() + "/conf/" + PICKLE_FOLDER = os.getcwd() + "/pickles/" + + + # Variables to track progress between iterations. + processed_series_but_yet_to_delete = list() + processed_and_deleted_series = list() + + + # Get features of a txt file + features_lists = list() + feature_files = list() + + # All processed series is saved between iterations as pickle files. + try: + with open(PICKLE_FOLDER + 'processed_series_but_yet_to_delete.pickle', 'rb') as f: + processed_series_but_yet_to_delete = pickle.load(f) + except: + logging.info("Unable to load a valid pickle file. Initialized with empty value for processed_series_but_yet_to_delete") + + try: + with open(PICKLE_FOLDER + 'processed_and_deleted_series.pickle', 'rb') as f: + processed_and_deleted_series = pickle.load(f) + except: + logging.info("Unable to load a valid pickle file. Initialized with empty value for processed_and_deleted_series") + + # Read the txt file which includes the features, then extract them + os.chdir(FEATURES_FOLDER) + txt_files = glob.glob('*.txt') + logging.info('Number of files consisting of the features to extract: %s', str(len(txt_files))) + + for file in txt_files: + filename, file_extension = os.path.splitext(file) + text_file = open(file, "r") + feature_list = text_file.read().split('\n') + del feature_list[-1] + features_lists.append(feature_list) + feature_files.append(filename) + + logging.info('The execution started at: %s', str(datetime.datetime.now())) + + logging.debug('Debug logs enabled.') + + # Create connections for communicating with Mongo DB server, to store metadata + try: + if os.environ['MONGO_URI'] != "": + mongo_uri = 'mongodb://' + os.environ['MONGO_URI'] + else: + mongo_uri = 'mongodb://' + sys.argv[1] + except KeyError: mongo_uri = 'mongodb://' + sys.argv[1] -except KeyError: - mongo_uri = 'mongodb://' + sys.argv[1] -client = pymongo.MongoClient(mongo_uri) -DB = client.ScannersInfo + client = pymongo.MongoClient(mongo_uri) + DB = client.ScannersInfo -# The thread scheduling -schedule.every(5).minutes.do(run_threaded, run_dcm4che) -schedule.every(1).hours.do(run_threaded, measure_diskutil) -schedule.every(1).day.at("23:59").do(run_threaded, clear_storage) -schedule.every(20).minutes.do(run_threaded, update_pickle) -schedule.every(10).minutes.do(run_threaded, extract) + # The thread scheduling + schedule.every(5).minutes.do(run_threaded, run_dcm4che) + schedule.every(1).hours.do(run_threaded, measure_diskutil) + schedule.every(1).day.at("23:59").do(run_threaded, clear_storage) + schedule.every(20).minutes.do(run_threaded, update_pickle) + schedule.every(10).minutes.do(run_threaded, extract) -# Keep running in a loop. -while True: - schedule.run_pending() - time.sleep(1) + # Keep running in a loop. + while True: + schedule.run_pending() + time.sleep(1) From 2dd50e7ef6fdb6284b89b6ab65d921872fd22b84 Mon Sep 17 00:00:00 2001 From: Chinmay Vibhute Date: Thu, 1 Jul 2021 23:06:24 +0530 Subject: [PATCH 38/46] meta extrn unit tests --- .gitignore | 3 +- tests/README.md | 9 +- tests/data/meta-extraction/input/.gitkeep | 0 tests/data/meta-extraction/input/no-img.dcm | 0 tests/unit/test_meta_extraction.py | 189 ++++++++++++++++++++ 5 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 tests/data/meta-extraction/input/.gitkeep create mode 100644 tests/data/meta-extraction/input/no-img.dcm create mode 100644 tests/unit/test_meta_extraction.py diff --git a/.gitignore b/.gitignore index f4858d0..85fa31e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ htmlcov coverage.xml /tests/data/tmp /tests/data/**/*.dcm -!/tests/data/**/no-img.dcm \ No newline at end of file +/tests/data/**/*.pickle +!/tests/data/**/no-img.dcm diff --git a/tests/README.md b/tests/README.md index 3580cfe..504ff51 100644 --- a/tests/README.md +++ b/tests/README.md @@ -10,12 +10,18 @@ pip install -r requirements-dev.txt Add the test data in `/tests/data//input` for respective tests. -### PNG Extraction Data Setup +### PNG Extraction Test Data Setup Test data in `/tests/data/png-extraction/input`. For unit tests, add a valid dcm file, with name `test-img.dcm`. +### Meta Extraction Test Data Setup + +Test data in `/tests/data/meta-extraction/input`. + +For unit tests, add a valid dcm file, with name `test-img.dcm`. + ## Running Tests Initialize the required data, and run tests from ``. @@ -31,4 +37,3 @@ pytest ./tests --cov=./modules --cov-report=html ``` and open the `/htmlcov/index.html` - diff --git a/tests/data/meta-extraction/input/.gitkeep b/tests/data/meta-extraction/input/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/data/meta-extraction/input/no-img.dcm b/tests/data/meta-extraction/input/no-img.dcm new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/test_meta_extraction.py b/tests/unit/test_meta_extraction.py new file mode 100644 index 0000000..52bb649 --- /dev/null +++ b/tests/unit/test_meta_extraction.py @@ -0,0 +1,189 @@ +import glob +import os +import pickle +import pytest +import pydicom +import sys + +from pathlib import Path, PurePath +from pytest_mock import MockerFixture + +niffler_modules_path = Path.cwd() / 'modules' +meta_extraction_path = niffler_modules_path / 'meta-extraction' +sys.path.append(str(meta_extraction_path)) +import MetadataExtractor + + +@pytest.fixture(autouse=True) +def mock_logger(mocker: MockerFixture): + return mocker.patch('MetadataExtractor.logging') + + +@pytest.fixture(autouse=True) +def mock_global_config(mocker: MockerFixture): + STORAGE_FOLDER = pytest.data_dir / 'meta-extraction' / 'storage_folder' + PICKLE_FOLDER = pytest.out_dir / 'meta-extraction' / 'pickles' + pytest.create_dirs(STORAGE_FOLDER, PICKLE_FOLDER) + return mocker.patch.multiple( + MetadataExtractor, + STORAGE_FOLDER=str(STORAGE_FOLDER), + PICKLE_FOLDER=str(PICKLE_FOLDER) + "/" + ) + + +@pytest.fixture +def features_lists(): + features_lists = list() + txt_files = glob.glob( + str(meta_extraction_path / 'conf') + '/*.txt' + ) + for file in txt_files: + text_file = open(file, "r") + feature_list = text_file.read().split('\n') + del feature_list[-1] + features_lists.append(feature_list) + return features_lists + + +@pytest.fixture +def feature_files(): + feature_files = list() + txt_files = glob.glob( + str(meta_extraction_path / 'conf') + '/*.txt' + ) + for file in txt_files: + filename, ext = os.path.splitext(file) + feature_files.append(filename) + return feature_files + + +class TestGetTuples: + test_dcm_file = str( + pytest.data_dir / 'meta-extraction' / 'input' / 'test-img.dcm') + test_valid_plan = pydicom.dcmread(test_dcm_file) + + def test_correct_output(self, features_lists): + for features in features_lists: + first_key = features[0] + tuple_list = MetadataExtractor.get_tuples( + self.test_valid_plan, features) + assert tuple_list[0][0] == first_key + + def test_correct_output_with_key(self, features_lists): + key = "rand_key" + for features in features_lists: + first_key = key + "_" + features[0] + tuple_list = MetadataExtractor.get_tuples( + self.test_valid_plan, features, key=key) + assert tuple_list[0][0] == first_key + + def test_feature_error(self, mock_logger): + invalid_features = ["some_invalid_feature"] + MetadataExtractor.get_tuples(self.test_valid_plan, invalid_features) + MetadataExtractor.logging.debug.assert_called_once() + + # TODO image with feature value of type pydicom.sequence.Sequence + # TODO minor code coverage + + +class TestGetDictFields: + test_dcm_file = str( + pytest.data_dir / 'meta-extraction' / 'input' / 'test-img.dcm') + test_valid_plan = pydicom.dcmread(test_dcm_file) + + def test_success(self, features_lists): + for features in features_lists: + tuple_list = MetadataExtractor.get_tuples( + self.test_valid_plan, features) + dict_fields = MetadataExtractor.get_dict_fields( + dict(tuple_list), features) + assert dict_fields[tuple_list[0][0]] == tuple_list[0][1] + + +class TestMeasureDiskUtil: + + def test_measure_diskutil(self, mock_logger, mock_global_config): + MetadataExtractor.measure_diskutil() + MetadataExtractor.logging.info.assert_called_once() + + +class TestUpdatePickle: + + def setup_method(self): + self.processed_series_but_yet_to_delete = [] + self.processed_and_deleted_series = [] + + def test_update_success(self, mocker, mock_logger, mock_global_config): + mocker.patch.multiple( + MetadataExtractor, + processed_series_but_yet_to_delete=self.processed_series_but_yet_to_delete, + processed_and_deleted_series=self.processed_and_deleted_series, + ) + # Function Completes write without error + MetadataExtractor.update_pickle() + MetadataExtractor.logging.debug.assert_called_once_with( + 'dumping complete') + + +class TestClearStorage: + + def setup_method(self): + self.processed_and_deleted_series = [] + self.processed_series_but_yet_to_delete = ['yet_to_delete'] + + @pytest.fixture(autouse=True) + def mock_series(self, mocker: MockerFixture): + return mocker.patch.multiple( + MetadataExtractor, + processed_series_but_yet_to_delete=self.processed_series_but_yet_to_delete, + processed_and_deleted_series=self.processed_and_deleted_series, + ) + + @pytest.fixture(autouse=True) + def mock_shutil(self, mocker: MockerFixture): + return mocker.patch('MetadataExtractor.shutil') + + def test_file_not_found_error(self, mock_shutil): + + mock_shutil.rmtree.side_effect = FileNotFoundError + + MetadataExtractor.clear_storage() + MetadataExtractor.logging.debug.assert_any_call( + 'The series of id %s was not found. Hence, not deleted in this iteration. Probably it was already deleted in a previous iteration without being tracked or by an external process', + self.processed_series_but_yet_to_delete[0] + ) + + def test_conn_reset_error(self, mock_shutil): + + mock_shutil.rmtree.side_effect = ConnectionResetError + + MetadataExtractor.clear_storage() + MetadataExtractor.logging.debug.assert_any_call( + 'The connection was reset during the deletion') + + def test_os_error(self, mock_shutil): + + mock_shutil.rmtree.side_effect = OSError + + MetadataExtractor.clear_storage() + MetadataExtractor.logging.debug.assert_any_call( + 'New images arriving for the series. Therefore, do not delete the series: %s', + self.processed_series_but_yet_to_delete[0] + ) + + def test_success(self): + MetadataExtractor.clear_storage() + MetadataExtractor.logging.debug.assert_any_call( + 'Deleted: %s %s %s %s', str(1), + ' out of ', + str(len(self.processed_series_but_yet_to_delete)), + ' remaining extraction completed series.' + ) + + +class TestRunThreaded: + + def test_thread_start_success(self, mocker): + stub = mocker.stub(name='job_func_stub') + MetadataExtractor.run_threaded(stub) + stub.assert_called_once() From a2d107c4c4c6dfd3acf35efa828d108292479a31 Mon Sep 17 00:00:00 2001 From: Ananth Reddy Date: Fri, 2 Jul 2021 14:03:02 -0400 Subject: [PATCH 39/46] Adding extractor shell file for rta-extraction(changed the filename) --- modules/rta-extraction/service/rtaextractor.sh | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100755 modules/rta-extraction/service/rtaextractor.sh diff --git a/modules/rta-extraction/service/rtaextractor.sh b/modules/rta-extraction/service/rtaextractor.sh new file mode 100755 index 0000000..94d659f --- /dev/null +++ b/modules/rta-extraction/service/rtaextractor.sh @@ -0,0 +1,7 @@ +#!/bin/bash +cd /opt/localdrive/Niffler/modules/rta-extraction/ +source ~/.bashrc +python3 RtaExtractor.py >> niffler-rta-extraction.out & +wait +echo "The Niffler RTA Extractor Process has failed" >> niffler-rta-extraction.out +echo "The Niffler RTA Extractor Process has failed" | mail -s "The Niffler RTA Extractor Process has failed" test@test.test From 86e16159e09172a51a9b985f94d30f82a12e9579 Mon Sep 17 00:00:00 2001 From: Ananth Reddy Date: Fri, 2 Jul 2021 14:04:43 -0400 Subject: [PATCH 40/46] Adding service file for rta-extraction --- modules/rta-extraction/service/rtaextractor.service | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 modules/rta-extraction/service/rtaextractor.service diff --git a/modules/rta-extraction/service/rtaextractor.service b/modules/rta-extraction/service/rtaextractor.service new file mode 100644 index 0000000..9ea191e --- /dev/null +++ b/modules/rta-extraction/service/rtaextractor.service @@ -0,0 +1,13 @@ +[Unit] +Description=rtaextractor service +Requires=mongod.service +After=network.target + +[Service] +Environment="MONGO_URI=USERNAME:PASSWORD@localhost:27017/" +Type=simple +ExecStart=/opt/localdrive/Niffler/modules/rta-extraction/service/rtaextractor.sh +TimeoutStartSec=360 + +[Install] +WantedBy=default.target From e613ce72f5d35a03caccea5d875495c4fb005d4d Mon Sep 17 00:00:00 2001 From: Ananth Reddy Date: Fri, 2 Jul 2021 14:09:35 -0400 Subject: [PATCH 41/46] Implemented data dumping, clearing(updating) and viewing modules --- modules/rta-extraction/RtaExtractor.py | 156 +++++++++++++++++++++++-- 1 file changed, 148 insertions(+), 8 deletions(-) diff --git a/modules/rta-extraction/RtaExtractor.py b/modules/rta-extraction/RtaExtractor.py index 86629ac..fbf4a2b 100644 --- a/modules/rta-extraction/RtaExtractor.py +++ b/modules/rta-extraction/RtaExtractor.py @@ -1,15 +1,28 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +""" + +This script: + 1 - starts dcm4che to receive images from remote PACS. + 2 - reads txt files containing feature list to identify research profiles consisting of the interesting attributes. + 3 - grabs one instance per series and extract the metadata for the identified attributes and store in MongoDB. + 4 - deletes the dicom images periodically once the metadata is extracted. + 5 - keeps track of the volume of the dcm storage folder +""" import os, os.path +from re import findall import sys import glob +from bson.objectid import ObjectId import pymongo +from pymongo.message import delete, query import pydicom import requests import schedule import time import datetime +import datetime import threading import logging import pickle @@ -17,8 +30,12 @@ import subprocess import pandas as pd import json +import time +from datetime import datetime, timedelta from pymongo import MongoClient +logging.basicConfig(filename='rta_extraction.logs', filemode='w', format='%(asctime)s - %(levelname)s - %(message)s') + with open('service/system.json', 'r') as f: niffler = json.load(f) @@ -32,15 +49,19 @@ Mongo_URI = niffler['MongoURI'] # Connect to MongoDB +connection_start_time = time.time() try: client = MongoClient(Mongo_URI) logging.info('MongoDB Connection Successful.') except: - logging.info('MongoDB Connection Unsuccessful.') + logging.error('MongoDB Connection Unsuccessful.') +logging.info('Time taken to establish MongoDB Connection - {}'.format(round(time.time() - connection_start_time), 2)) db = client.database +# Data Dumping Functions def dump_labs_json_data(): + labs_dump_time = time.time() labs_collection = db.labs_json for file in os.listdir(Labs_FolderPath): if file.endswith('.json'): @@ -48,13 +69,14 @@ def dump_labs_json_data(): f = open(Labs_FilePath, 'r') labs_data = json.load(f) - labs_collection.insert_one(labs_data) f.close() - + labs_collection.insert_one(labs_data) + labs_collection.create_index('lab_date') logging.info('Labs data is dumped into MongoDB. The collection name is - labs_json') - return (labs_collection, labs_data) + logging.info('Time taken to dump labs data in MongoDB - {}'.format(round(time.time() - labs_dump_time), 2)) def dump_meds_json_data(): + meds_dump_time = time.time() meds_collection = db.meds_json for file in os.listdir(Meds_FolderPath): if file.endswith('.json'): @@ -64,11 +86,11 @@ def dump_meds_json_data(): meds_data = json.load(f) meds_collection.insert_one(meds_data) f.close() - logging.info('Meds data is dumped into MongoDB. The collection name is - meds_json') - return (meds_collection, meds_data) + logging.info('Time taken to dump meds data in MongoDB - {}'.format(round(time.time() - meds_dump_time), 2)) def dump_orders_json_data(): + orders_dump_time = time.time() orders_collection = db.orders_json for file in os.listdir(Orders_FolderPath): if file.endswith('.json'): @@ -78,14 +100,132 @@ def dump_orders_json_data(): orders_data = json.load(f) orders_collection.insert_one(orders_data) f.close() - logging.info('Orders data is dumped into MongoDB. The collection name is - orders_json') - return (orders_collection, orders_data) + logging.info('Time taken to dump orders data in MongoDB - {}'.format(round(time.time() - orders_dump_time), 2)) def run_threaded(job_func): job_thread = threading.Thread(target=job_func) job_thread.start() +# Clean Storage Functions +def clean_labs_collection(): + labs_clear_time = time.time() + labs_collection = db.labs_json + items_collection = labs_collection.find_one()['items'] + + cut_off_time = datetime.now() - timedelta(days=1) + cut_off_date = cut_off_time.date() + print (cut_off_date) + for item in items_collection: + lab_datetime = datetime.strptime(item['lab_date'][:-1], '%Y-%m-%dT%H:%M:%S') + lab_datetime = lab_datetime.date() + if (lab_datetime < cut_off_date): + items_collection.remove(item) + + item_id = labs_collection.find_one()['_id'] + db.labs_json.update_one({'_id':ObjectId(item_id)}, {'$set':{'items':items_collection}}) + logging.info('Time taken to clear labs data in MongoDB - {}'.format(time.time() - labs_clear_time)) + +def clean_meds_collection(): + meds_clear_time = time.time() + meds_collection = db.meds_json + items_collection = meds_collection.find_one()['items'] + + cut_off_time = datetime.now() - timedelta(days=1) + cut_off_date = cut_off_time.date() + print (cut_off_date) + for item in items_collection: + meds_datetime = datetime.strptime(item['update_dt_tm'][:-1], '%Y-%m-%dT%H:%M:%S') + meds_datetime = meds_datetime.date() + if (meds_datetime < cut_off_date): + items_collection.remove(item) + + item_id = meds_collection.find_one()['_id'] + db.meds_json.update_one({'_id':ObjectId(item_id)}, {'$set':{'items':items_collection}}) + logging.info('Time taken to clear meds data in MongoDB - {}'.format(time.time() - meds_clear_time)) + +def clean_orders_collection(): + orders_clean_time = time.time() + orders_collection = db.orders_json + items_collection = orders_collection.find_one()['items'] + + cut_off_time = datetime.now() - timedelta(days=1) + cut_off_date = cut_off_time.date() + print (cut_off_date) + for item in items_collection: + orders_datetime = datetime.strptime(item['requested_dt_tm'][:-1], '%Y-%m-%dT%H:%M:%S') + orders_datetime = orders_datetime.date() + if (orders_datetime < cut_off_date): + items_collection.remove(item) + + item_id = orders_collection.find_one()['_id'] + db.orders_json.update_one({'_id':ObjectId(item_id)}, {'$set':{'items':items_collection}}) + logging.info('Time taken to clear orders data in MongoDB - {}'.format(time.time() - orders_clean_time)) + schedule.every(Labs_ExtractionFrequency).minutes.do(run_threaded, dump_labs_json_data) schedule.every(Meds_ExtractionFrequency).minutes.do(run_threaded, dump_meds_json_data) schedule.every(Orders_ExtractionFrequency).minutes.do(run_threaded, dump_orders_json_data) + +schedule.every(1).day.at("23:59").do(run_threaded, clean_labs_collection) +schedule.every(1).day.at("23:59").do(run_threaded, clean_meds_collection) +schedule.every(1).day.at("23:59").do(run_threaded, clean_orders_collection) + +# Parse json file in python +def extract_labs_data(labs_user_query=None): + labs_collection = db.labs_json + labs_cursor = labs_collection.find({}) + for document in labs_cursor: + labs_data = document + + list_dict = [] + if (labs_user_query): + required_columns = labs_user_query + else: + required_columns = labs_data['items'].keys() + + for i in range(len(labs_data['items'])): + list_dict.append(labs_data['items'][i]) + + labs_df = pd.DataFrame(list_dict) + labs_df = labs_df[required_columns] + print (labs_df.head()) + +def extract_meds_data(meds_user_query): + meds_collection = db.meds_json + meds_cursor = meds_collection.find({}) + for document in meds_cursor: + meds_data = document + + list_dict = [] + if (meds_user_query): + required_columns = meds_user_query + else: + required_columns = meds_data['items'].keys() + + for i in range(len(meds_data['items'])): + list_dict.append(meds_data['items'][i]) + + meds_df = pd.DataFrame(list_dict) + meds_df = meds_df[required_columns] + print (meds_df.head()) + +def extract_orders_data(orders_user_query): + orders_collection = db.orders_json + orders_cursor = orders_collection.find({}) + for document in orders_cursor: + orders_data = document + + list_dict = [] + if (orders_user_query): + required_columns = orders_user_query + else: + required_columns = orders_data['items'].keys() + + for i in range(len(orders_data['items'])): + list_dict.append(orders_data['items'][i]) + + orders_df = pd.DataFrame(list_dict) + orders_df = orders_df[required_columns] + print (orders_df.head()) + +extract_labs_data(labs_user_query=['empi', 'lab', 'result_val']) From 2a29dfa54fc5e98f0434712cfe5ea6a5fb2c7778 Mon Sep 17 00:00:00 2001 From: Ananth Reddy Date: Fri, 2 Jul 2021 14:23:15 -0400 Subject: [PATCH 42/46] Removed print statements from modules (as per the code policy) --- modules/rta-extraction/RtaExtractor.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/modules/rta-extraction/RtaExtractor.py b/modules/rta-extraction/RtaExtractor.py index fbf4a2b..306b720 100644 --- a/modules/rta-extraction/RtaExtractor.py +++ b/modules/rta-extraction/RtaExtractor.py @@ -22,7 +22,6 @@ import schedule import time import datetime -import datetime import threading import logging import pickle @@ -115,7 +114,6 @@ def clean_labs_collection(): cut_off_time = datetime.now() - timedelta(days=1) cut_off_date = cut_off_time.date() - print (cut_off_date) for item in items_collection: lab_datetime = datetime.strptime(item['lab_date'][:-1], '%Y-%m-%dT%H:%M:%S') lab_datetime = lab_datetime.date() @@ -133,7 +131,6 @@ def clean_meds_collection(): cut_off_time = datetime.now() - timedelta(days=1) cut_off_date = cut_off_time.date() - print (cut_off_date) for item in items_collection: meds_datetime = datetime.strptime(item['update_dt_tm'][:-1], '%Y-%m-%dT%H:%M:%S') meds_datetime = meds_datetime.date() @@ -151,7 +148,6 @@ def clean_orders_collection(): cut_off_time = datetime.now() - timedelta(days=1) cut_off_date = cut_off_time.date() - print (cut_off_date) for item in items_collection: orders_datetime = datetime.strptime(item['requested_dt_tm'][:-1], '%Y-%m-%dT%H:%M:%S') orders_datetime = orders_datetime.date() @@ -188,7 +184,7 @@ def extract_labs_data(labs_user_query=None): labs_df = pd.DataFrame(list_dict) labs_df = labs_df[required_columns] - print (labs_df.head()) + return (labs_df.head()) def extract_meds_data(meds_user_query): meds_collection = db.meds_json @@ -207,7 +203,7 @@ def extract_meds_data(meds_user_query): meds_df = pd.DataFrame(list_dict) meds_df = meds_df[required_columns] - print (meds_df.head()) + return (meds_df.head()) def extract_orders_data(orders_user_query): orders_collection = db.orders_json @@ -226,6 +222,6 @@ def extract_orders_data(orders_user_query): orders_df = pd.DataFrame(list_dict) orders_df = orders_df[required_columns] - print (orders_df.head()) + return (orders_df.head()) extract_labs_data(labs_user_query=['empi', 'lab', 'result_val']) From 9629ab69acf54a9d8c6b3c4f176be19107c77a08 Mon Sep 17 00:00:00 2001 From: Pradeeban Kathiravelu Date: Fri, 2 Jul 2021 23:30:43 -0400 Subject: [PATCH 43/46] Naming best practices --- modules/cold-extraction/ColdDataRetriever.py | 2 +- modules/frontend/server.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/cold-extraction/ColdDataRetriever.py b/modules/cold-extraction/ColdDataRetriever.py index 0edd43f..8a532a5 100644 --- a/modules/cold-extraction/ColdDataRetriever.py +++ b/modules/cold-extraction/ColdDataRetriever.py @@ -15,7 +15,7 @@ from collections import defaultdict -def initialize_Values(valuesDict): +def initialize_config_and_execute(valuesDict): global storescp_processes, niffler_processes, nifflerscp_str, qbniffler_str global storage_folder, file_path, csv_file, extraction_type, accession_index, patient_index, date_index, date_type, date_format, email, send_email, system_json global DCM4CHE_BIN, SRC_AET, QUERY_AET, DEST_AET, NIGHTLY_ONLY, START_HOUR, END_HOUR, IS_EXTRACTION_NOT_RUNNING, NIFFLER_ID, MAX_PROCESSES, SEPARATOR diff --git a/modules/frontend/server.py b/modules/frontend/server.py index 0a24bc7..bdf4d81 100644 --- a/modules/frontend/server.py +++ b/modules/frontend/server.py @@ -182,7 +182,7 @@ def cold_extraction(): import io sys.path.append("../cold-extraction/") import ColdDataRetriever - x = ColdDataRetriever.initialize_Values(cold_extraction_values) + x = ColdDataRetriever.initialize_config_and_execute(cold_extraction_values) return render_template('cold_extraction.html', logs = logs, files_list = files_present_in_server) else: return render_template('cold_extraction.html', logs = logs, files_list = files_present_in_server) From 7d85a64aeb056e7f6a844f3315afce18668bdeb9 Mon Sep 17 00:00:00 2001 From: Pradeeban Kathiravelu Date: Sat, 3 Jul 2021 02:43:35 -0400 Subject: [PATCH 44/46] Remove redundant outdated comment --- modules/png-extraction/ImageExtractorSlurm.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/modules/png-extraction/ImageExtractorSlurm.py b/modules/png-extraction/ImageExtractorSlurm.py index 319819f..16c6f6d 100644 --- a/modules/png-extraction/ImageExtractorSlurm.py +++ b/modules/png-extraction/ImageExtractorSlurm.py @@ -1,12 +1,5 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -""" -This code creates a dataframe of dicom headers based on dicom files in a filepath. -This code also extracts the images within those dicoms if requested. see section 'print images' -pip3 install image numpy pandas pydicom pillow pypng -Make sure to have empty extracted-images, failed-dicom/1, failed-dicom/2, failed-dicom/3 folders -ready in the root folder. -""" import time import pdb import numpy as np From 883464f5a9808109f4857a3b32c1302fb11f2194 Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Sat, 3 Jul 2021 15:57:58 +0530 Subject: [PATCH 45/46] Fixes redundant variables declaration --- modules/cold-extraction/ColdDataRetriever.py | 77 +------------------- 1 file changed, 4 insertions(+), 73 deletions(-) diff --git a/modules/cold-extraction/ColdDataRetriever.py b/modules/cold-extraction/ColdDataRetriever.py index 8a532a5..117e157 100644 --- a/modules/cold-extraction/ColdDataRetriever.py +++ b/modules/cold-extraction/ColdDataRetriever.py @@ -292,11 +292,6 @@ def run_cold_extraction(): sys.exit(0) if __name__ == "__main__": - global storescp_processes, niffler_processes, nifflerscp_str, qbniffler_str - global storage_folder, file_path, csv_file, extraction_type, accession_index, patient_index, date_index, date_type, date_format, email, send_email - global DCM4CHE_BIN, SRC_AET, QUERY_AET, DEST_AET, NIGHTLY_ONLY, START_HOUR, END_HOUR, IS_EXTRACTION_NOT_RUNNING, NIFFLER_ID, MAX_PROCESSES, SEPARATOR - global accessions, patients, dates, niffler_log, resume, length - config = defaultdict(lambda: None) # Read Default config.json file with open('config.json', 'r') as f: @@ -333,71 +328,7 @@ def run_cold_extraction(): args = vars(ap.parse_args()) - #Get variables for StoreScp from config.json. - storage_folder = args['StorageFolder'] - file_path = args['FilePath'] - - # Get variables for the each on-demand extraction from config.json - csv_file = args['CsvFile'] - extraction_type = args['ExtractionType'] - accession_index = args['AccessionIndex'] - patient_index = args['PatientIndex'] - date_index = args['DateIndex'] - date_type = args['DateType'] - date_format = args['DateFormat'] - email = args['YourEmail'] - send_email = args['SendEmail'] - - # Reads the system_json file. - system_json = args['NifflerSystem'] - - with open(system_json, 'r') as f: - niffler = json.load(f) - - # Get constants from system.json - DCM4CHE_BIN = niffler['DCM4CHEBin'] - SRC_AET = niffler['SrcAet'] - QUERY_AET = niffler['QueryAet'] - DEST_AET = niffler['DestAet'] - NIGHTLY_ONLY = niffler['NightlyOnly'] - START_HOUR = niffler['StartHour'] - END_HOUR = niffler['EndHour'] - IS_EXTRACTION_NOT_RUNNING = True - NIFFLER_ID = niffler['NifflerID'] - MAX_PROCESSES = niffler['MaxNifflerProcesses'] - - SEPARATOR = ',' - - accessions = [] - patients = [] - dates = [] - - storescp_processes = 0 - niffler_processes = 0 - - nifflerscp_str = "storescp.*{0}".format(QUERY_AET) - qbniffler_str = 'ColdDataRetriever' - - niffler_log = 'niffler' + str(NIFFLER_ID) + '.log' - - logging.basicConfig(filename=niffler_log,level=logging.INFO) - logging.getLogger('schedule').setLevel(logging.WARNING) - - # Variables to track progress between iterations. - extracted_ones = list() - - # By default, assume that this is a fresh extraction. - resume = False - - # All extracted files from the csv file are saved in a respective .pickle file. - try: - with open(csv_file +'.pickle', 'rb') as f: - extracted_ones = pickle.load(f) - # Since we have successfully located a pickle file, it indicates that this is a resume. - resume = True - except: - logging.info("No existing pickle file found. Therefore, initialized with empty value to track the progress to {0}.pickle.".format(csv_file)) - - # record the start time - t_start = time.time() - run_cold_extraction() + if(len(args) > 0): + initialize_config_and_execute(args) + else: + initialize_config_and_execute(config) \ No newline at end of file From 27a626cbaf3b8974f5aef82cd05e4fd6644f97e0 Mon Sep 17 00:00:00 2001 From: Nishchal-007 Date: Sat, 3 Jul 2021 19:42:24 +0530 Subject: [PATCH 46/46] Fixed Minor bug related to naming issue --- modules/cold-extraction/ColdDataRetriever.py | 20 ++++++++++---------- modules/frontend/server.py | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/modules/cold-extraction/ColdDataRetriever.py b/modules/cold-extraction/ColdDataRetriever.py index 117e157..dacf926 100644 --- a/modules/cold-extraction/ColdDataRetriever.py +++ b/modules/cold-extraction/ColdDataRetriever.py @@ -21,17 +21,17 @@ def initialize_config_and_execute(valuesDict): global DCM4CHE_BIN, SRC_AET, QUERY_AET, DEST_AET, NIGHTLY_ONLY, START_HOUR, END_HOUR, IS_EXTRACTION_NOT_RUNNING, NIFFLER_ID, MAX_PROCESSES, SEPARATOR global accessions, patients, dates, niffler_log, resume, length - storage_folder = valuesDict['storage_folder'] - file_path = valuesDict['file_path'] + storage_folder = valuesDict['StorageFolder'] + file_path = valuesDict['FilePath'] csv_file = valuesDict['CsvFile'] - extraction_type = valuesDict['extraction_type'] - accession_index = int(valuesDict['accession_index']) - patient_index = int(valuesDict['patient_index']) - date_index = int(valuesDict['date_index']) - date_type = valuesDict['date_type'] - date_format = valuesDict['date_format'] - email = valuesDict['email'] - send_email = bool(valuesDict['send_email']) + extraction_type = valuesDict['ExtractionType'] + accession_index = int(valuesDict['AccessionIndex']) + patient_index = int(valuesDict['PatientIndex']) + date_index = int(valuesDict['DateIndex']) + date_type = valuesDict['DateType'] + date_format = valuesDict['DateFormat'] + email = valuesDict['YourEmail'] + send_email = bool(valuesDict['SendEmail']) system_json = valuesDict['NifflerSystem'] # Reads the system_json file. diff --git a/modules/frontend/server.py b/modules/frontend/server.py index bdf4d81..39a05f3 100644 --- a/modules/frontend/server.py +++ b/modules/frontend/server.py @@ -167,16 +167,16 @@ def cold_extraction(): if checkfile: cold_extraction_values['NifflerSystem'] = NifflerSystem_File - cold_extraction_values['storage_folder'] = request.form['StorageFolder'] - cold_extraction_values['file_path'] = file_path - cold_extraction_values['extraction_type'] = request.form['ExtractionType'] - cold_extraction_values['accession_index'] = accession_index - cold_extraction_values['patient_index'] = patient_index - cold_extraction_values['date_index'] = date_index - cold_extraction_values['date_type'] = request.form['DateType'] - cold_extraction_values['date_format'] = date_format - cold_extraction_values['send_email'] = request.form['sendEmail'] - cold_extraction_values['email'] = request.form['email'] + cold_extraction_values['StorageFolder'] = request.form['StorageFolder'] + cold_extraction_values['FilePath'] = file_path + cold_extraction_values['ExtractionType'] = request.form['ExtractionType'] + cold_extraction_values['AccessionIndex'] = accession_index + cold_extraction_values['PatientIndex'] = patient_index + cold_extraction_values['DateIndex'] = date_index + cold_extraction_values['DateType'] = request.form['DateType'] + cold_extraction_values['DateFormat'] = date_format + cold_extraction_values['SendEmail'] = request.form['sendEmail'] + cold_extraction_values['YourEmail'] = request.form['email'] import sys import io