-
Notifications
You must be signed in to change notification settings - Fork 68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SVCS-86] Add Papaya Renderer for Dicom and NifTi Files #265
base: develop
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TomBaxter A few thoughts and suggestions, thanks.
-
TheUpdate: it seems that including JS locally is how the way we built the MFR at the beginning. In the future we will have a package manager for JS. For now, it looks good.papaya.js
is super huge. Is there a way that it is loaded as a package or at least from ascript
tag which retrieves the content from a trusted third party domain or maybe our CDN domain (still in progress I think) that holds all the scripts/resources. Similar for thepapaya.css
though it is small. -
The project you mentioned now support
Nifti1
but still not.nii.gz
or the.dcm
. Is this worth trying? -
For
mfr/extensions/papaya/static/data
, does the temporary files only exist during file rendering? When are they cleaned? Don't want this to take up server memory.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First pass done 🎆 with a few questions and suggestions. Back to you @TomBaxter .
|
||
def test_render_papaya(self, renderer, metadata, assets_url): | ||
body = renderer.render() | ||
print(body) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
return PapayaRenderer(metadata, file_path, url, assets_url, export_url) | ||
|
||
|
||
class TestPapayaRenderer: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we expand the tests to cover at least valid and invalid files:
- Valid files
- If there are any potential version issues/differences, we should include them as well.
- Invalid/Corrupted files that may throw exceptions during opening, parsing, rendering, e.t.c.
- We should be able to handle exceptions and return a relevant error message.
This applies to all three types. (Dicom, Compressed and Uncompressed NifTi).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added tests for other two file extensions. However, not that interesting as this only tests the python rendering of the HTML. I don't believe there is a way to test the javascript from pytest. Therefore, didn't bother adding an Invalid/Corrupted test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. This is the render instead of the exporter. No need for the invalid files.
mfr/extensions/papaya/render.py
Outdated
for data_file in os.listdir(self.data_dir): | ||
if data_file == '.gitignore': | ||
continue | ||
if (time.time() - os.path.getctime(self.data_dir + data_file)) >= self.data_old: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make this a general util method for checking file time expiration for MFR in general?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
# Ignore everything in this directory | ||
* | ||
# Except this file | ||
!.gitignore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should ignore the directory and all its sub directories and contents instead of having such a .gitignore
. In this way, no need to check:
if data_file == '.gitignore':
continue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what you're getting at here. In any case, the .gitignore was for my convenience while developing. Removing it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example, here is a directory /repo/level_1/level_2/level_3/
. If you want to ignore all files in level_3/
, instead of having a level3/.gitignore
you can use a level2/.gitignore
or even a repo level one that ignores everything in the directory level3/
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Travis failed, which reminded me of why I had the .gitignore. In addition to it being handy during development, I also need it to create and check in the empty directory mfr/extensions/papaya/static/data/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 I will locally test it again today before sending this to PCR.
mfr/extensions/papaya/render.py
Outdated
class PapayaRenderer(extension.BaseRenderer): | ||
|
||
data_dir = settings.DATA_DIR | ||
data_old = settings.DATA_OLD |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about data_exp
and DATA_EXP
for expiration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, OLD doesn't really fit. But EXP doesn't either. Went with TTL (Time To Live).
]).get_template('viewer.mako') | ||
|
||
def render(self): | ||
self.remove_old_files() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mean the temporary files for rendering will get stuck here all the time? Each time users try to render a file, the old ones gets removed but new ones are created.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that is correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how much this will impact prod memory. MFR and WB pods constantly run out of space though. I will leave this for Phase 2 Review.
@@ -0,0 +1 @@ | |||
.papaya{width:90%;height:90%;margin:25px auto;background-color:black;font-family:sans-serif}.papaya:before{position:relative;content:"Papaya requires JavaScript...";display:block;top:45%;color:red;margin:0 auto;font-size:18px;font-family:sans-serif}.papaya-fullscreen{height:100%}.papaya-toolbar{text-align:left;box-sizing:content-box;position:relative}.papaya-toolbar ul{margin:0;list-style:none}.papaya-toolbar input[type=file]{text-align:right;display:none}.papaya-kiosk-controls{position:relative;margin:5px auto;list-style:none;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.papaya-kiosk-controls ul{list-style:none}.papaya-kiosk-controls button{-webkit-appearance:none;border-radius:0;-webkit-border-radius:0;font-size:14px;height:25px;background-color:lightgray}.papaya-control-increment{-webkit-appearance:none;border-radius:0;-webkit-border-radius:0;font-size:14px;height:25px;width:25px;text-align:center;vertical-align:middle;padding:0;margin-left:auto;margin-right:auto;line-height:16px;box-sizing:border-box;font-family:"Courier New",Courier,monospace}.papaya-main-increment{-webkit-appearance:none;border-radius:0;-webkit-border-radius:0;font-size:14px;height:25px;width:25px;text-align:center;font-family:"Courier New",Courier,monospace;background-color:lightgray;vertical-align:middle;padding:0;margin-left:auto;margin-right:auto;box-sizing:border-box;outline:0}.papaya-main-decrement{-webkit-appearance:none;border-radius:0;-webkit-border-radius:0;font-size:14px;height:25px;width:25px;text-align:center;font-family:"Courier New",Courier,monospace;background-color:lightgray;vertical-align:middle;padding:0;margin-left:auto;margin-right:auto;box-sizing:border-box;outline:0}.papaya-main-swap{-webkit-appearance:none;border-radius:0;-webkit-border-radius:0;font-size:14px;height:25px;background-color:lightgray;outline:0}.papaya-main-goto-center{-webkit-appearance:none;border-radius:0;-webkit-border-radius:0;font-size:14px;height:25px;background-color:lightgray;outline:0}.papaya-main-goto-origin{-webkit-appearance:none;border-radius:0;-webkit-border-radius:0;font-size:14px;height:25px;background-color:lightgray;outline:0}.papaya-slider-slice{padding:0 5px;display:inline}.papaya-slider-slice span{font-size:14px;font-family:sans-serif;vertical-align:middle}.papaya-slider-slice button{-webkit-appearance:none;border-radius:0;-webkit-border-radius:0;vertical-align:middle;font-size:14px;height:25px;background-color:lightgray}.papaya-controlbar-label{color:#000}.papaya-menu{width:220px;background:#222;z-index:100;position:absolute;border:solid 2px darkgray;padding:4px;margin:0}.papaya-menu li{font-size:12px;font-family:sans-serif;padding:4px 2px;color:#b5cbd3;cursor:pointer;list-style-type:none}.papaya-menu-label{font-size:14px;font-family:sans-serif;font-weight:bold;padding:2px 8px;cursor:pointer;vertical-align:text-bottom}.papaya-menu-titlebar{font-size:16px;font-family:sans-serif;padding:3px 8px 0 8px;cursor:default;vertical-align:text-bottom;color:white}.papaya-menu-icon{margin-left:5px}.papaya-menu-icon img{box-sizing:content-box}.papaya-menu-hovering{background-color:#444}.papaya-menu-spacer{height:8px}.papaya-menu-unselectable{-moz-user-select:-moz-none;-khtml-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;-webkit-user-drag:none;user-drag:none}.papaya-menu-button-hovering{background-color:#DDD}.papaya-menu-filechooser{cursor:pointer;width:200px;display:inline-block;font-weight:normal}.papaya-menu-input{width:38px;margin-right:5px;color:black}li .papaya-menu-slider{vertical-align:middle;text-align:center;display:inline;width:120px;padding:0;margin:0}.papaya-dialog{min-width:400px;max-width:500px;height:500px;background:#222;position:absolute;z-index:100;border:solid 2px darkgray;padding:6px;font-size:14px;font-family:sans-serif;color:#b5cbd3;box-sizing:content-box;line-height:1.45}.papaya-dialog-content{margin:20px;height:415px;color:#dedede;overflow:auto;-ms-overflow-style:-ms-autohiding-scrollbar}.papaya-dialog-content-nowrap{white-space:nowrap}.papaya-dialog-content table{margin:0 auto}.papaya-dialog-content-label{text-align:right;padding:5px;color:#b5cbd3}.papaya-dialog-content-control{text-align:left;padding:5px}.papaya-dialog-content-help{text-align:right;padding:5px;color:lightgray;font-size:12px}.papaya-dialog-title{color:#b5cbd3;font-weight:bold;font-size:16px}.papaya-dialog-button{text-align:right;box-sizing:content-box;height:22px}.papaya-dialog-button button{box-sizing:content-box;color:black;font-size:11px}.papaya-dialog-background{position:fixed;top:0;left:0;background-color:#fff;width:100%;height:100%;opacity:.5}.papaya-dialog-stopscroll{height:100%;overflow:hidden}.checkForJS{width:90%;height:90%;margin:25px auto;background-color:black}.checkForJS:before{position:relative;content:"Papaya requires JavaScript...";display:block;top:45%;color:red;margin:0 auto;font-size:18px;font-family:sans-serif;text-align:center}.papaya-utils-unsupported{width:90%;height:90%;margin:25px auto;background-color:black}.papaya-utils-unsupported-message{position:relative;display:block;top:45%;color:red;margin:0 auto;font-size:18px;font-family:sans-serif;text-align:center}.papaya-viewer{line-height:1;font-family:sans-serif}.papaya-viewer div,.papaya-viewer canvas{margin:0;padding:0;border:0;font:inherit;font-size:100%;vertical-align:baseline;font-family:sans-serif}.papaya-viewer canvas{cursor:crosshair} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add an empty line at the end of the file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file was copied straight from GitHub. I'm not knowledgeable about CSS, are you sure that is the right thing to do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -0,0 +1,2105 @@ | |||
var PAPAYA_BUILD_NUM="1430",papayaLoadableImages=[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are several different version of released code. Is this the smallest file that covers our need. (e.g. If their minimal version has what we need, we can use that one (1K+) instead of this (2K+).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that is a question for product team. I demoed, and they appeared to like the added functionality.
Add customization to papaya library to disallow opening of new files from user desktop. Consulted with Product Team. They determined that it would be confusing and possibly dangerous to allow opening of files from user desktop, when they might confuse it with the WaterButler repository. Used a custom built papaya.js built on https://github.com/TomBaxter/Papaya/tree/feature/noNewFiles I have sent them a PR, hopefully they bite. rii-mango/Papaya#101
There is an "entrypoints" test that needs to be updated if you add new file types. Don't worry about it and I will have it along with my PR. |
…cs-86 [SVCS-86] Add Papaya Renderer for Dicom and NifTi Files
[#SVCS-86]
[SVCS-86] Papaya Render
[#SVCS-86]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good and works as expected. 🎆 🎆 Move to PCR.
@felliott @TomBaxter
There is one issue for Phase 2 on removing old files.
Temporary files are generated during rendering. It never gets deleted automatically. The solution in this PR is to remove old files in the temp directory at the beginning of each rendering. Here is a few concerns:
- There is always old files there.
- If more than one person try to render a file (and both try to clean the old files), only one of them will grab the lock while others hanging? Not able to tigger this locally but might be an issue on
staging
(less likely) andprod
(likely).
Suggestions: we schedule a periodic task in celery to clean them?
Keep in mind that the files MFR downloads get named with a UUID, so overlap is extremely unlikely. |
After discussing with @cslzchen , I understand his concern is that if two renderers try to delete a file that one will error since the other would have already deleted the file. An idea for handling this could be to simply handle the OSError exception with a try/except/pass . |
@@ -0,0 +1,7 @@ | |||
Currently using a custom built papaya.js with option to remove the ability to open new files from the users desktop. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: Remove this file. PR was accepted and we are no longer using custom build.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉 Nicely done!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned, the PR that customized the JS lib has been accepted. This ticket and PR should be updated to reflect the changes. Please rebase and fix conflicts along the way. h/t to @TomBaxter and I will take over.
Purpose:
Create renderer for NifTI images. (.nii)
Collateral
Supports .nii.gz (NifTi files are commonly saved with .gz)
Supports .dcm (DICOM images)
Changes:
Create papaya renderer in /mfr/extensions/papaya
Relies on https://github.com/rii-mango/Papaya
Copies file to render to /mfr/extensions/papaya/static/data to make available to papaya.js
NiFTI files are often saved as .nii.gz by convention. I've added a facility to handle file extensions with an additional extension, such as compression.
At the beginning of each render files older then 5 minutes will be deleted.
Added a test.
Side effects
Some rendered file data will temporarily exist in mfr/extensions/papaya/static/data
Notes
I created PR for our use case...
rii-mango/Papaya#101
Which was accepted as of Papaya release build-1430
Use papaya.js and papaya.css from /Papaya-build-1430/release/current/standard/
We should keep an eye on this project...
https://github.com/FNNDSC/ami
We may want to switch to it in the future.
Tickets
https://openscience.atlassian.net/browse/SVCS-86