girder-slicer-cli-web 1.5.15.dev8__tar.gz → 1.5.15.dev12__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/PKG-INFO +3 -1
  2. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/README.rst +2 -0
  3. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/girder_slicer_cli_web.egg-info/PKG-INFO +3 -1
  4. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/girder_slicer_cli_web.egg-info/SOURCES.txt +2 -0
  5. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/girder_slicer_cli_web.egg-info/scm_file_list.json +2 -0
  6. girder_slicer_cli_web-1.5.15.dev12/girder_slicer_cli_web.egg-info/scm_version.json +8 -0
  7. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/girder_worker_plugin/direct_docker_run.py +24 -0
  8. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/image_job.py +1 -1
  9. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/prepare_task.py +66 -16
  10. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/rest_slicer_cli.py +3 -0
  11. girder_slicer_cli_web-1.5.15.dev12/tests/data/MultipleSpec.xml +25 -0
  12. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/girder_worker_plugin/test_direct_docker_run.py +31 -0
  13. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/test_docker.py +11 -0
  14. girder_slicer_cli_web-1.5.15.dev12/tests/test_multiple_input.py +88 -0
  15. girder_slicer_cli_web-1.5.15.dev8/girder_slicer_cli_web.egg-info/scm_version.json +0 -8
  16. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/.circleci/config.yml +0 -0
  17. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/.dockerignore +0 -0
  18. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/.editorconfig +0 -0
  19. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/.gitignore +0 -0
  20. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/.pre-commit-config.yaml +0 -0
  21. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/LICENSE +0 -0
  22. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/codecov.yml +0 -0
  23. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/docs/worker_management.rst +0 -0
  24. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/example-average-color/.dockerignore +0 -0
  25. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/example-average-color/Dockerfile +0 -0
  26. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/example-average-color/README.rst +0 -0
  27. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/example-average-color/average_color/average_color.py +0 -0
  28. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/example-average-color/average_color/average_color.xml +0 -0
  29. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/example-average-color/cli_list.json +0 -0
  30. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/example-average-color/cli_list.py +0 -0
  31. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/example-girder-requests/Dockerfile +0 -0
  32. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/example-girder-requests/README.rst +0 -0
  33. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/example-girder-requests/cli_list.json +0 -0
  34. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/example-girder-requests/cli_list.py +0 -0
  35. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/example-girder-requests/girder_requests/girder_requests.py +0 -0
  36. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/example-girder-requests/girder_requests/girder_requests.xml +0 -0
  37. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/girder_slicer_cli_web.egg-info/dependency_links.txt +0 -0
  38. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/girder_slicer_cli_web.egg-info/entry_points.txt +0 -0
  39. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/girder_slicer_cli_web.egg-info/not-zip-safe +0 -0
  40. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/girder_slicer_cli_web.egg-info/requires.txt +0 -0
  41. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/girder_slicer_cli_web.egg-info/top_level.txt +0 -0
  42. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/setup.cfg +0 -0
  43. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/setup.py +0 -0
  44. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/__init__.py +0 -0
  45. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/cli_list_entrypoint.py +0 -0
  46. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/cli_utils.py +0 -0
  47. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/config.py +0 -0
  48. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/ctk_cli_adjustment.py +0 -0
  49. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/docker_resource.py +0 -0
  50. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/girder_plugin.py +0 -0
  51. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/girder_worker_plugin/__init__.py +0 -0
  52. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/girder_worker_plugin/cli_progress.py +0 -0
  53. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/models/__init__.py +0 -0
  54. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/models/docker_image.py +0 -0
  55. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/models/exceptions.py +0 -0
  56. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/models/json_to_xml.py +0 -0
  57. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/models/parser.py +0 -0
  58. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/models/schema.json +0 -0
  59. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/JobStatus.js +0 -0
  60. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/collections/WidgetCollection.js +0 -0
  61. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/collections/index.js +0 -0
  62. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/events.js +0 -0
  63. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/index.js +0 -0
  64. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/main.js +0 -0
  65. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/models/WidgetModel.js +0 -0
  66. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/models/index.js +0 -0
  67. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/package.json +0 -0
  68. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/parser/constraints.js +0 -0
  69. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/parser/convert.js +0 -0
  70. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/parser/defaultValue.js +0 -0
  71. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/parser/group.js +0 -0
  72. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/parser/index.js +0 -0
  73. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/parser/panel.js +0 -0
  74. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/parser/param.js +0 -0
  75. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/parser/parse.js +0 -0
  76. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/parser/widget.js +0 -0
  77. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/routes.js +0 -0
  78. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/stylesheets/configView.styl +0 -0
  79. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/stylesheets/controlWidget.styl +0 -0
  80. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/stylesheets/controlsPanel.styl +0 -0
  81. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/stylesheets/outputParameterDialog.styl +0 -0
  82. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/stylesheets/panel.styl +0 -0
  83. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/stylesheets/panelGroup.styl +0 -0
  84. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/stylesheets/slicerUI.styl +0 -0
  85. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/templates/booleanWidget.pug +0 -0
  86. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/templates/colorWidget.pug +0 -0
  87. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/templates/configView.pug +0 -0
  88. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/templates/controlsPanel.pug +0 -0
  89. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/templates/enumerationWidget.pug +0 -0
  90. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/templates/fileWidget.pug +0 -0
  91. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/templates/jobsListWidget.pug +0 -0
  92. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/templates/outputParameterDialog.pug +0 -0
  93. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/templates/panel.pug +0 -0
  94. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/templates/panelGroup.pug +0 -0
  95. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/templates/rangeWidget.pug +0 -0
  96. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/templates/regionWidget.pug +0 -0
  97. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/templates/slicerUI.pug +0 -0
  98. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/templates/uploadImageDialog.pug +0 -0
  99. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/templates/widget.pug +0 -0
  100. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/utils.js +0 -0
  101. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/views/CollectionView.js +0 -0
  102. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/views/ConfigView.js +0 -0
  103. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/views/ControlWidget.js +0 -0
  104. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/views/ControlsPanel.js +0 -0
  105. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/views/ItemSelectorWidget.js +0 -0
  106. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/views/ItemView.js +0 -0
  107. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/views/JobsListWidget.js +0 -0
  108. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/views/JobsPanel.js +0 -0
  109. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/views/OutputParameterDialog.js +0 -0
  110. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/views/Panel.js +0 -0
  111. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/views/PanelGroup.js +0 -0
  112. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/views/index.js +0 -0
  113. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/web_client/views/utils.js +0 -0
  114. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/slicer_cli_web/worker_tools.py +0 -0
  115. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/.dockerignore +0 -0
  116. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/Dockerfile +0 -0
  117. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/Example1/Example1.json +0 -0
  118. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/Example1/Example1.py +0 -0
  119. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/Example1/Example1.xml +0 -0
  120. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/Example1/Example1.yaml +0 -0
  121. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/Example2/Example2.json +0 -0
  122. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/Example2/Example2.py +0 -0
  123. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/Example2/Example2.xml +0 -0
  124. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/Example2/Example2.yaml +0 -0
  125. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/Example3/Example3.json +0 -0
  126. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/Example3/Example3.py +0 -0
  127. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/Example3/Example3.xml +0 -0
  128. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/Example3/Example3.yaml +0 -0
  129. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/ExampleProgress/ExampleProgress.json +0 -0
  130. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/ExampleProgress/ExampleProgress.py +0 -0
  131. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/ExampleProgress/progress_helper.py +0 -0
  132. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/ExampleSubJob/ExampleSubJob.json +0 -0
  133. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/ExampleSubJob/ExampleSubJob.py +0 -0
  134. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/cli_list.json +0 -0
  135. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/small-docker/cli_list.py +0 -0
  136. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/__init__.py +0 -0
  137. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/conftest.py +0 -0
  138. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/data/ExampleSpec.xml +0 -0
  139. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/data/girder_worker.cfg +0 -0
  140. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/data/parser_params_advanced.json +0 -0
  141. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/data/parser_params_advanced.xml +0 -0
  142. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/data/parser_params_advanced.yaml +0 -0
  143. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/data/parser_params_simple.json +0 -0
  144. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/data/parser_params_simple.xml +0 -0
  145. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/data/parser_params_simple.yaml +0 -0
  146. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/data/parser_simple.json +0 -0
  147. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/data/parser_simple.xml +0 -0
  148. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/data/parser_simple.yaml +0 -0
  149. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/girder_worker_plugin/test_cli_progress.py +0 -0
  150. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/test_batch.py +0 -0
  151. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/test_config.py +0 -0
  152. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/test_load.py +0 -0
  153. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/test_parser.py +0 -0
  154. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/test_rest_slicer_cli.py +0 -0
  155. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/test_web_client.py +0 -0
  156. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/web_client_specs/.eslintrc +0 -0
  157. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/web_client_specs/configViewSpec.js +0 -0
  158. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/web_client_specs/containerViewSpec.js +0 -0
  159. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/web_client_specs/dockerTaskSpec.js +0 -0
  160. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/web_client_specs/itemSelectorWidgetSpec.js +0 -0
  161. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/web_client_specs/panelGroupSpec.js +0 -0
  162. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/web_client_specs/parseSpec.js +0 -0
  163. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tests/web_client_specs/widgetSpec.js +0 -0
  164. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/tox.ini +0 -0
  165. {girder_slicer_cli_web-1.5.15.dev8 → girder_slicer_cli_web-1.5.15.dev12}/utils/xmltojson.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: girder-slicer-cli-web
3
- Version: 1.5.15.dev8
3
+ Version: 1.5.15.dev12
4
4
  Summary: A girder plugin for exposing slicer CLIs over the web
5
5
  Home-page: https://github.com/girder/slicer_cli_web
6
6
  Author: Kitware, Inc.
@@ -130,6 +130,8 @@ The XML must conform to the `Slicer Execution Schema <https://www.slicer.org/w/i
130
130
  ``multi`` allow multiple shapes, indicated by separating coordinates of each shape by -1,-1. Note that neither -1,-1 nor -2,-2 are allowed as coordinates within a shape -- to use those, specify them with decimals (e.g., -1.0,-1.0).
131
131
  The submit options will add suggestions on how the UI should handle changes. If present, the option to auto-run a job as soon as a valid shape is set should be present. ``autosubmit`` means this should always happen. ``submit`` or ``submitoff`` offers this as a setting but is default to not submit the job. ``submiton`` offers this as a setting and defaults to submitting the job.
132
132
 
133
+ - Girder-resource input types (``image``, ``file``, ``item``, ``directory``) can have a ``multiple`` property. If ``true``, the parameter accepts a comma-separated list of Girder IDs. Each resource is bound into the container separately (using direct read-only mounts when possible) and the CLI receives a single comma-separated list of container paths, in the submitted order. Inputs with ``multiple`` set are not available for batch processing.
134
+
133
135
  - Some input types (``image``, ``file``, ``item``, ``directory``) can have ``defaultNameMatch``, ``defaultPathMatch``, and ``defaultRelativePath`` properties. The first two are regular expressions designed to give a UI a value to match to prepopulate default values from files or paths that match the regex. ``defaultNameMatch`` is intended to match the final path element, whereas ``defaultPathMatch`` is used on the entire path as a combined string. ``defaultRelativePath`` is used to find a value that has a path relative to some base. In the Girder UI, this might be from an item.
134
136
 
135
137
  - Input types can have a ``datalist`` property. If this is present, when the CLI is first loaded or, possibly periodically after parameters have been changed, the CLI may be called with optional parameters. The CLI is expected to return a new-line separated list of values that can be used as recommended inputs. As an example, a ``string`` input might have a ``datalist`` of ``--enumerate-options``; the cli would be called with the existing parameters PLUS the extra parameter specified by ``datalist``. If the result is sensible, the input control would expose this list to the user. The ``datalist`` property is a json-encoded dictionary that overrides other parameters. This should override parameters that aren't needed to be resolved to produce the datalist (e.g., input and output files) as that will speed up the call. The CLI should respond to the modified call with a response that contains multiple ``<element>some text</element>`` values that will be the suggested data for the control.
@@ -81,6 +81,8 @@ The XML must conform to the `Slicer Execution Schema <https://www.slicer.org/w/i
81
81
  ``multi`` allow multiple shapes, indicated by separating coordinates of each shape by -1,-1. Note that neither -1,-1 nor -2,-2 are allowed as coordinates within a shape -- to use those, specify them with decimals (e.g., -1.0,-1.0).
82
82
  The submit options will add suggestions on how the UI should handle changes. If present, the option to auto-run a job as soon as a valid shape is set should be present. ``autosubmit`` means this should always happen. ``submit`` or ``submitoff`` offers this as a setting but is default to not submit the job. ``submiton`` offers this as a setting and defaults to submitting the job.
83
83
 
84
+ - Girder-resource input types (``image``, ``file``, ``item``, ``directory``) can have a ``multiple`` property. If ``true``, the parameter accepts a comma-separated list of Girder IDs. Each resource is bound into the container separately (using direct read-only mounts when possible) and the CLI receives a single comma-separated list of container paths, in the submitted order. Inputs with ``multiple`` set are not available for batch processing.
85
+
84
86
  - Some input types (``image``, ``file``, ``item``, ``directory``) can have ``defaultNameMatch``, ``defaultPathMatch``, and ``defaultRelativePath`` properties. The first two are regular expressions designed to give a UI a value to match to prepopulate default values from files or paths that match the regex. ``defaultNameMatch`` is intended to match the final path element, whereas ``defaultPathMatch`` is used on the entire path as a combined string. ``defaultRelativePath`` is used to find a value that has a path relative to some base. In the Girder UI, this might be from an item.
85
87
 
86
88
  - Input types can have a ``datalist`` property. If this is present, when the CLI is first loaded or, possibly periodically after parameters have been changed, the CLI may be called with optional parameters. The CLI is expected to return a new-line separated list of values that can be used as recommended inputs. As an example, a ``string`` input might have a ``datalist`` of ``--enumerate-options``; the cli would be called with the existing parameters PLUS the extra parameter specified by ``datalist``. If the result is sensible, the input control would expose this list to the user. The ``datalist`` property is a json-encoded dictionary that overrides other parameters. This should override parameters that aren't needed to be resolved to produce the datalist (e.g., input and output files) as that will speed up the call. The CLI should respond to the modified call with a response that contains multiple ``<element>some text</element>`` values that will be the suggested data for the control.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: girder-slicer-cli-web
3
- Version: 1.5.15.dev8
3
+ Version: 1.5.15.dev12
4
4
  Summary: A girder plugin for exposing slicer CLIs over the web
5
5
  Home-page: https://github.com/girder/slicer_cli_web
6
6
  Author: Kitware, Inc.
@@ -130,6 +130,8 @@ The XML must conform to the `Slicer Execution Schema <https://www.slicer.org/w/i
130
130
  ``multi`` allow multiple shapes, indicated by separating coordinates of each shape by -1,-1. Note that neither -1,-1 nor -2,-2 are allowed as coordinates within a shape -- to use those, specify them with decimals (e.g., -1.0,-1.0).
131
131
  The submit options will add suggestions on how the UI should handle changes. If present, the option to auto-run a job as soon as a valid shape is set should be present. ``autosubmit`` means this should always happen. ``submit`` or ``submitoff`` offers this as a setting but is default to not submit the job. ``submiton`` offers this as a setting and defaults to submitting the job.
132
132
 
133
+ - Girder-resource input types (``image``, ``file``, ``item``, ``directory``) can have a ``multiple`` property. If ``true``, the parameter accepts a comma-separated list of Girder IDs. Each resource is bound into the container separately (using direct read-only mounts when possible) and the CLI receives a single comma-separated list of container paths, in the submitted order. Inputs with ``multiple`` set are not available for batch processing.
134
+
133
135
  - Some input types (``image``, ``file``, ``item``, ``directory``) can have ``defaultNameMatch``, ``defaultPathMatch``, and ``defaultRelativePath`` properties. The first two are regular expressions designed to give a UI a value to match to prepopulate default values from files or paths that match the regex. ``defaultNameMatch`` is intended to match the final path element, whereas ``defaultPathMatch`` is used on the entire path as a combined string. ``defaultRelativePath`` is used to find a value that has a path relative to some base. In the Girder UI, this might be from an item.
134
136
 
135
137
  - Input types can have a ``datalist`` property. If this is present, when the CLI is first loaded or, possibly periodically after parameters have been changed, the CLI may be called with optional parameters. The CLI is expected to return a new-line separated list of values that can be used as recommended inputs. As an example, a ``string`` input might have a ``datalist`` of ``--enumerate-options``; the cli would be called with the existing parameters PLUS the extra parameter specified by ``datalist``. If the result is sensible, the input control would expose this list to the user. The ``datalist`` property is a json-encoded dictionary that overrides other parameters. This should override parameters that aren't needed to be resolved to produce the datalist (e.g., input and output files) as that will speed up the call. The CLI should respond to the modified call with a response that contains multiple ``<element>some text</element>`` values that will be the suggested data for the control.
@@ -134,10 +134,12 @@ tests/test_batch.py
134
134
  tests/test_config.py
135
135
  tests/test_docker.py
136
136
  tests/test_load.py
137
+ tests/test_multiple_input.py
137
138
  tests/test_parser.py
138
139
  tests/test_rest_slicer_cli.py
139
140
  tests/test_web_client.py
140
141
  tests/data/ExampleSpec.xml
142
+ tests/data/MultipleSpec.xml
141
143
  tests/data/girder_worker.cfg
142
144
  tests/data/parser_params_advanced.json
143
145
  tests/data/parser_params_advanced.xml
@@ -123,6 +123,7 @@
123
123
  "tests/__init__.py",
124
124
  "tests/conftest.py",
125
125
  "tests/data/ExampleSpec.xml",
126
+ "tests/data/MultipleSpec.xml",
126
127
  "tests/data/girder_worker.cfg",
127
128
  "tests/data/parser_params_advanced.json",
128
129
  "tests/data/parser_params_advanced.xml",
@@ -139,6 +140,7 @@
139
140
  "tests/test_config.py",
140
141
  "tests/test_docker.py",
141
142
  "tests/test_load.py",
143
+ "tests/test_multiple_input.py",
142
144
  "tests/test_parser.py",
143
145
  "tests/test_rest_slicer_cli.py",
144
146
  "tests/test_web_client.py",
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "1.5.14",
3
+ "distance": 12,
4
+ "node": "g9da9ba6b2d9936a01e7f529ca0d51e4ce6e53566",
5
+ "dirty": false,
6
+ "branch": "master",
7
+ "node_date": "2026-10-23"
8
+ }
@@ -8,6 +8,7 @@ from girder_worker.docker.tasks import DockerTask, _docker_run
8
8
  from girder_worker.docker.transforms import BindMountVolume, ContainerStdOut
9
9
  from girder_worker.docker.transforms.girder import GirderFileIdToVolume
10
10
  from girder_worker_utils import _walk_obj
11
+ from girder_worker_utils.transform import Transform
11
12
  from girder_worker_utils.transforms.girder_io import GirderClientTransform
12
13
 
13
14
  from .cli_progress import CLIProgressCLIWriter
@@ -67,6 +68,26 @@ class DirectGirderFileIdToVolume(GirderFileIdToVolume):
67
68
  return super().transform(**kwargs)
68
69
 
69
70
 
71
+ class CommaJoinedVolumes(Transform):
72
+ """One container argument spanning several volume transforms: their
73
+ resolved container paths, comma-joined in order."""
74
+
75
+ def __init__(self, volumes):
76
+ self._volumes = volumes
77
+
78
+ def transform(self, **kwargs):
79
+ return ','.join(str(volume.transform(**kwargs)) for volume in self._volumes)
80
+
81
+ def cleanup(self, **kwargs):
82
+ for volume in self._volumes:
83
+ volume.cleanup(**kwargs)
84
+
85
+ def _repr_model_(self):
86
+ return '<%s.%s: [%s]>' % (
87
+ self.__module__, self.__class__.__name__,
88
+ ', '.join(volume._repr_model_() for volume in self._volumes))
89
+
90
+
70
91
  class GirderApiUrl(GirderClientTransform):
71
92
  def transform(self, **kwargs):
72
93
  return self.gc.urlBase
@@ -85,6 +106,9 @@ def _resolve_direct_file_paths(args, kwargs):
85
106
  path = arg.resolve_direct_file_path()
86
107
  if path:
87
108
  extra_volumes.append(path)
109
+ elif isinstance(arg, CommaJoinedVolumes):
110
+ for volume in arg._volumes:
111
+ resolve(volume)
88
112
  return arg
89
113
  _walk_obj(args, resolve)
90
114
  _walk_obj(kwargs, resolve)
@@ -134,7 +134,7 @@ def jobPullAndLoad(job):
134
134
  log='Started to Load Docker images\n',
135
135
  status=JobStatus.RUNNING,
136
136
  )
137
- user = User().load(job['userId'], level=AccessType.READ, user=job['userId'])
137
+ user = User().load(job['userId'], force=True, exc=True)
138
138
  baseFolder = Folder().load(
139
139
  job['kwargs']['folder'], user=user, level=AccessType.WRITE, exc=True)
140
140
 
@@ -18,7 +18,7 @@ OPENAPI_DIRECT_TYPES = {'boolean', 'integer', 'float', 'double', 'string'}
18
18
  FOLDER_SUFFIX = '_folder'
19
19
 
20
20
 
21
- def _to_file_volume(param, model):
21
+ def _to_file_volume(param, model, filename=None, gc=None, direct_path=None):
22
22
  from girder_worker.docker.transforms.girder import (GirderFileIdToVolume,
23
23
  GirderFolderIdToVolume,
24
24
  GirderItemIdToVolume)
@@ -29,19 +29,55 @@ def _to_file_volume(param, model):
29
29
  girder_type = SLICER_TYPE_TO_GIRDER_MODEL_MAP[param.typ]
30
30
 
31
31
  if girder_type == 'folder':
32
- return GirderFolderIdToVolume(model['_id'], folder_name=model['name'])
32
+ return GirderFolderIdToVolume(model['_id'], folder_name=filename or model['name'],
33
+ gc=gc)
33
34
  elif girder_type == 'item':
34
- return GirderItemIdToVolume(model['_id'])
35
+ return GirderItemIdToVolume(model['_id'], gc=gc)
35
36
 
36
- if not Setting().get(PluginSettings.DIRECT_PATH):
37
- return GirderFileIdToVolume(model['_id'], filename=model['name'])
37
+ filename = filename or model['name']
38
+ if direct_path is None:
39
+ direct_path = Setting().get(PluginSettings.DIRECT_PATH)
40
+ if not direct_path:
41
+ return GirderFileIdToVolume(model['_id'], filename=filename, gc=gc)
38
42
 
39
43
  try:
40
44
  path = File().getLocalFilePath(model)
41
45
  return DirectGirderFileIdToVolume(model['_id'], direct_file_path=path,
42
- filename=model['name'])
46
+ filename=filename, gc=gc)
43
47
  except FilePathException:
44
- return GirderFileIdToVolume(model['_id'], filename=model['name'])
48
+ return GirderFileIdToVolume(model['_id'], filename=filename, gc=gc)
49
+
50
+
51
+ def _list_item_name(model):
52
+ # Id-prefix so same-named files in a list can't collide when mounted, and
53
+ # drop commas so a name can't corrupt the comma-joined path argument.
54
+ return ('%s_%s' % (model['_id'], model['name'])).replace(',', '_')
55
+
56
+
57
+ def _to_file_volume_arg(param, value):
58
+ from girder_worker.girder_plugin.constants import PluginSettings
59
+
60
+ from .girder_worker_plugin.direct_docker_run import CommaJoinedVolumes
61
+
62
+ if not isinstance(value, list):
63
+ return _to_file_volume(param, value)
64
+ # The direct-path setting is constant across the list; read it once.
65
+ direct_path = Setting().get(PluginSettings.DIRECT_PATH)
66
+ volumes = []
67
+ gc = None
68
+ for model in value:
69
+ volume = _to_file_volume(param, model, filename=_list_item_name(model),
70
+ gc=gc, direct_path=direct_path)
71
+ # Share the first volume's Girder client with the rest of the list so
72
+ # a large list does not mint one access token per file.
73
+ gc = volume.gc or gc
74
+ volumes.append(volume)
75
+ return CommaJoinedVolumes(volumes)
76
+
77
+
78
+ def _first_model(value):
79
+ """A possibly-multiple Girder input uses its first model as the primary."""
80
+ return value[0] if isinstance(value, list) else value
45
81
 
46
82
 
47
83
  def _to_girder_api(param, value):
@@ -56,7 +92,7 @@ def _to_girder_api(param, value):
56
92
  return value
57
93
 
58
94
 
59
- def _parseParamValue(param, value, user, token):
95
+ def _parseParamValue(param, value, user, token, primary_only=False):
60
96
  if isinstance(value, bytes):
61
97
  value = value.decode('utf8')
62
98
 
@@ -64,10 +100,21 @@ def _parseParamValue(param, value, user, token):
64
100
  if is_on_girder(param):
65
101
  girder_type = SLICER_TYPE_TO_GIRDER_MODEL_MAP[param.typ]
66
102
  curModel = ModelImporter.model(girder_type)
67
- loaded = curModel.load(value, level=AccessType.READ, user=user)
68
- if not loaded:
69
- raise RestException('Invalid %s id (%s).' % (curModel.name, str(value)))
70
- return loaded
103
+
104
+ def load(value):
105
+ loaded = curModel.load(value, level=AccessType.READ, user=user)
106
+ if not loaded:
107
+ raise RestException('Invalid %s id (%s).' % (curModel.name, str(value)))
108
+ return loaded
109
+
110
+ if getattr(param, 'multiple', None):
111
+ ids = [subvalue.strip() for subvalue in str(value).split(',')]
112
+ # Callers that only use the primary (first) model can skip loading
113
+ # the rest of the list.
114
+ if primary_only:
115
+ ids = ids[:1]
116
+ return [load(subvalue) for subvalue in ids]
117
+ return load(value)
71
118
 
72
119
  try:
73
120
  if param.isVector():
@@ -150,7 +197,7 @@ def _add_optional_input_param(param, args, user, token, templateParams):
150
197
 
151
198
  if is_on_girder(param):
152
199
  # Bindings
153
- container_args.append(_to_file_volume(param, value))
200
+ container_args.append(_to_file_volume_arg(param, value))
154
201
  elif is_girder_api(param):
155
202
  # Bindings
156
203
  container_args.append(_to_girder_api(param, value))
@@ -200,7 +247,7 @@ def _add_indexed_input_param(param, args, user, token, templateParams=None):
200
247
 
201
248
  if is_on_girder(param):
202
249
  # Bindings
203
- return _to_file_volume(param, value), value['name']
250
+ return _to_file_volume_arg(param, value), _first_model(value)['name']
204
251
  if is_girder_api(param):
205
252
  return _to_girder_api(param, value), value['name']
206
253
  value = _processTemplates(value, param, templateParams)
@@ -249,9 +296,11 @@ def _populateTemplateParams(params, user, token, index_params, opt_params, templ
249
296
  for param in index_params + opt_params:
250
297
  if param.identifier() in params:
251
298
  try:
252
- value = _parseParamValue(param, params[param.identifier()], user, token)
299
+ value = _parseParamValue(param, params[param.identifier()], user, token,
300
+ primary_only=True)
253
301
  except Exception:
254
302
  continue
303
+ value = _first_model(value)
255
304
  value = value.get('name') if isinstance(value, dict) else value
256
305
  if value:
257
306
  templateParams[f'parameter_{param.name}'] = value
@@ -301,7 +350,8 @@ def prepare_task(params, user, token, index_params, opt_params,
301
350
  SLICER_TYPE_TO_GIRDER_MODEL_MAP[param.typ] != 'folder'):
302
351
  primary_input_name = name
303
352
  reference['userId'] = str(user['_id'])
304
- value = _parseParamValue(param, params[param.identifier()], user, token)
353
+ value = _first_model(_parseParamValue(
354
+ param, params[param.identifier()], user, token, primary_only=True))
305
355
  itemId = value['_id']
306
356
  if SLICER_TYPE_TO_GIRDER_MODEL_MAP[param.typ] == 'file':
307
357
  reference['fileId'] = str(value['_id'])
@@ -78,6 +78,9 @@ def _addInputParamToHandler(param, handlerDesc, required=True):
78
78
  'Girder ID of input %s (if batch input, this is a regex '
79
79
  'for item names) - %s: %s'
80
80
  % (param.typ, param.identifier(), param.description))
81
+ elif getattr(param, 'multiple', None):
82
+ desc = 'Comma separated Girder IDs of input %s - %s: %s' % (
83
+ param.typ, param.identifier(), param.description)
81
84
  else:
82
85
  desc = 'Girder ID of input %s - %s: %s' % (
83
86
  param.typ, param.identifier(), param.description)
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <executable>
3
+ <category>Test</category>
4
+ <title>Assembles A Series</title>
5
+ <description>Consumes a multi-file series and produces a single output image.</description>
6
+ <version>0.1.0</version>
7
+ <parameters>
8
+ <label>IO</label>
9
+ <description>Input/output parameters.</description>
10
+ <image multiple="true">
11
+ <name>inputSeries</name>
12
+ <label>Input Series</label>
13
+ <channel>input</channel>
14
+ <index>0</index>
15
+ <description>Files of the input series</description>
16
+ </image>
17
+ <image>
18
+ <name>outputImage</name>
19
+ <label>Output Image</label>
20
+ <channel>output</channel>
21
+ <index>1</index>
22
+ <description>Assembled output image</description>
23
+ </image>
24
+ </parameters>
25
+ </executable>
@@ -3,6 +3,7 @@ from os.path import basename
3
3
  import pytest
4
4
 
5
5
  from slicer_cli_web.girder_worker_plugin.direct_docker_run import (TEMP_VOLUME_DIRECT_MOUNT_PREFIX,
6
+ CommaJoinedVolumes,
6
7
  DirectGirderFileIdToVolume, run)
7
8
 
8
9
 
@@ -54,3 +55,33 @@ def test_direct_docker_run(mocker, server, adminToken, file):
54
55
  assert kwargs['container_args'] == [target_path]
55
56
  # volumes
56
57
  assert len(kwargs['volumes']) == 2
58
+
59
+
60
+ @pytest.mark.plugin('slicer_cli_web')
61
+ def test_direct_docker_run_multiple_files(mocker, server, file):
62
+ from girder.models.file import File
63
+
64
+ docker_run_mock = mocker.patch(
65
+ 'slicer_cli_web.girder_worker_plugin.direct_docker_run._docker_run')
66
+ docker_run_mock.return_value = []
67
+
68
+ gc_mock = MockedGirderClient()
69
+
70
+ path = File().getLocalFilePath(file)
71
+
72
+ run(image='test', container_args=[CommaJoinedVolumes([
73
+ DirectGirderFileIdToVolume(file['_id'], filename='direct.dat',
74
+ direct_file_path=path, gc=gc_mock),
75
+ DirectGirderFileIdToVolume(file['_id'], filename='downloaded.dat',
76
+ direct_file_path=None, gc=gc_mock),
77
+ ])])
78
+
79
+ docker_run_mock.assert_called_once()
80
+ kwargs = docker_run_mock.call_args[1]
81
+ first, second = kwargs['container_args'][0].split(',')
82
+ assert first == '%s/direct.dat' % TEMP_VOLUME_DIRECT_MOUNT_PREFIX
83
+ # the second file has no direct path, so it falls back to a download
84
+ assert second.endswith('/downloaded.dat')
85
+ assert not second.startswith(TEMP_VOLUME_DIRECT_MOUNT_PREFIX)
86
+ # one bind mount and the temporary volume
87
+ assert len(kwargs['volumes']) == 2
@@ -2,6 +2,7 @@ import json
2
2
 
3
3
  import docker
4
4
  import pytest
5
+ from girder.models.user import User
5
6
  from girder_jobs.constants import JobStatus
6
7
  from pytest_girder.assertions import assertStatus
7
8
  from pytest_girder.utils import getResponseBody
@@ -34,6 +35,16 @@ def testDockerAdd(images, server):
34
35
  images.assertNoImages()
35
36
 
36
37
 
38
+ @pytest.mark.plugin('slicer_cli_web')
39
+ def testDockerAddPrivateAdmin(images, admin):
40
+ User().setPublic(admin, False, save=True)
41
+
42
+ img_name = 'girder/slicer_cli_web:small'
43
+ images.addImage(img_name, JobStatus.SUCCESS)
44
+ images.endpointsExist(img_name, ['Example1', 'Example2', 'Example3'])
45
+ images.deleteImage(img_name, True)
46
+
47
+
37
48
  @pytest.mark.plugin('slicer_cli_web')
38
49
  def testDockerAddBadParam(server, admin, folder):
39
50
  # test sending bad parameters to the PUT endpoint
@@ -0,0 +1,88 @@
1
+ import functools
2
+ import io
3
+ import json
4
+ import os
5
+
6
+ import cherrypy
7
+ import pytest
8
+ from girder.api import rest
9
+ from girder.models.item import Item
10
+ from girder.models.token import Token
11
+ from girder.models.upload import Upload
12
+
13
+ from slicer_cli_web import docker_resource, rest_slicer_cli
14
+ from slicer_cli_web.models import CLIItem
15
+
16
+
17
+ @pytest.fixture
18
+ def seriesFiles(folder, admin, fsAssetstore):
19
+ files = []
20
+ for idx in range(2):
21
+ sampleData = b'Sample %d' % idx
22
+ files.append(Upload().uploadFromFile(
23
+ obj=io.BytesIO(sampleData), size=len(sampleData),
24
+ name='Series%d.dcm' % idx, parentType='folder', parent=folder,
25
+ user=admin))
26
+ yield files
27
+
28
+
29
+ @pytest.fixture
30
+ def multipleHandlerFunc(server, admin, folder):
31
+ # Make a request to allow there to be some lingering context to handle the
32
+ # testing outside of a request for the actual handler.
33
+ server.request('/system/version')
34
+ rest.setCurrentUser(admin)
35
+ cherrypy.request.params['token'] = Token().createToken(admin)['_id']
36
+
37
+ xmlpath = os.path.join(os.path.dirname(__file__), 'data', 'MultipleSpec.xml')
38
+
39
+ girderCLIItem = Item().createItem('multi', admin, folder)
40
+ Item().setMetadata(girderCLIItem, dict(
41
+ slicerCLIType='task', type='python', image='dockerImage',
42
+ digest='dockerImage@sha256:abc', xml=open(xmlpath, 'rb').read()))
43
+
44
+ resource = docker_resource.DockerResource('test')
45
+ item = CLIItem(girderCLIItem)
46
+ handlerFunc = rest_slicer_cli.genHandlerToRunDockerCLI(item)
47
+ yield functools.partial(handlerFunc, resource)
48
+
49
+
50
+ @pytest.mark.plugin('slicer_cli_web')
51
+ def test_multiple_input_fans_out(folder, seriesFiles, multipleHandlerFunc):
52
+ job = multipleHandlerFunc(params={
53
+ 'inputSeries': '%s,%s' % (seriesFiles[0]['_id'], seriesFiles[1]['_id']),
54
+ 'outputImage_folder': str(folder['_id']),
55
+ 'outputImage': 'out.png',
56
+ })
57
+
58
+ # The job title uses the first file of the list as the primary input
59
+ assert job['title'] == 'Assembles A Series on Series0.dcm'
60
+
61
+ kwargs = json.loads(job['kwargs'])
62
+ container_args = kwargs['container_args']
63
+ # cli name, one argument covering the whole series, and the output path
64
+ assert len(container_args) == 3
65
+ serialized = repr(container_args[1])
66
+ assert str(seriesFiles[0]['_id']) in serialized
67
+ assert str(seriesFiles[1]['_id']) in serialized
68
+
69
+
70
+ @pytest.mark.plugin('slicer_cli_web')
71
+ def test_multiple_input_comma_in_name(folder, admin, fsAssetstore, multipleHandlerFunc):
72
+ # A comma is legal in a Girder file name but is also the delimiter of the
73
+ # joined container-path argument, so it must not survive into the mounted
74
+ # filename.
75
+ sampleData = b'Sample'
76
+ commaFile = Upload().uploadFromFile(
77
+ obj=io.BytesIO(sampleData), size=len(sampleData), name='Series,part.dcm',
78
+ parentType='folder', parent=folder, user=admin)
79
+
80
+ job = multipleHandlerFunc(params={
81
+ 'inputSeries': str(commaFile['_id']),
82
+ 'outputImage_folder': str(folder['_id']),
83
+ 'outputImage': 'out.png',
84
+ })
85
+
86
+ serialized = repr(json.loads(job['kwargs'])['container_args'][1])
87
+ assert 'Series_part.dcm' in serialized
88
+ assert 'Series,part.dcm' not in serialized
@@ -1,8 +0,0 @@
1
- {
2
- "tag": "1.5.14",
3
- "distance": 8,
4
- "node": "g910aaded421a4433bd406bee4768bf3d0ac7aa30",
5
- "dirty": false,
6
- "branch": "master",
7
- "node_date": "2026-10-01"
8
- }