dcoraid 1.1.2__tar.gz → 1.2.0__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 (315) hide show
  1. {dcoraid-1.1.2 → dcoraid-1.2.0}/.github/workflows/check.yml +1 -1
  2. {dcoraid-1.1.2 → dcoraid-1.2.0}/.gitignore +5 -0
  3. {dcoraid-1.1.2 → dcoraid-1.2.0}/CHANGELOG +85 -1
  4. {dcoraid-1.1.2 → dcoraid-1.2.0}/MANIFEST.in +1 -1
  5. {dcoraid-1.1.2/dcoraid.egg-info → dcoraid-1.2.0}/PKG-INFO +1 -2
  6. {dcoraid-1.1.2 → dcoraid-1.2.0}/build-recipes/hook-dcoraid.py +1 -1
  7. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/_version.py +3 -3
  8. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/api/__init__.py +1 -0
  9. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/api/ckan_api.py +47 -12
  10. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/api/dataset.py +42 -133
  11. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/api/errors.py +0 -12
  12. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/api/s3_api.py +73 -23
  13. dcoraid-1.2.0/dcoraid/api/users.py +60 -0
  14. dcoraid-1.2.0/dcoraid/bagit/archive.py +354 -0
  15. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/bagit/info.py +2 -0
  16. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/bagit/manifest.py +18 -11
  17. dcoraid-1.2.0/dcoraid/cli/__init__.py +3 -0
  18. dcoraid-1.2.0/dcoraid/cli/task_archive_circle.py +145 -0
  19. dcoraid-1.1.2/dcoraid/cli.py → dcoraid-1.2.0/dcoraid/cli/task_upload_task.py +46 -25
  20. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/dbmodel/db_api.py +130 -34
  21. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/dbmodel/db_api_cached.py +34 -8
  22. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/dbmodel/db_core.py +32 -23
  23. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/dbmodel/extract.py +10 -3
  24. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/dbmodel/meta_cache.py +20 -22
  25. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/dbmodel/meta_cache_datasets.py +3 -2
  26. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/dbmodel/meta_cache_sqlite.py +2 -0
  27. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/download/job.py +21 -15
  28. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/download/queue.py +3 -4
  29. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/api.py +39 -1
  30. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/dbview/filter_base.py +28 -25
  31. dcoraid-1.2.0/dcoraid/gui/dbview/filter_base_ui.py +81 -0
  32. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/dbview/filter_chain.py +31 -32
  33. dcoraid-1.2.0/dcoraid/gui/dbview/filter_chain_ui.py +62 -0
  34. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/dbview/filter_views.py +26 -26
  35. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/logs/widget_log.py +15 -16
  36. dcoraid-1.2.0/dcoraid/gui/logs/widget_log_ui.py +47 -0
  37. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/main.py +54 -32
  38. dcoraid-1.2.0/dcoraid/gui/main_ui.py +166 -0
  39. dcoraid-1.1.2/dcoraid/gui/panel_downloads/widget_download.py → dcoraid-1.2.0/dcoraid/gui/panel_downloads/download_table.py +2 -146
  40. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_downloads/widget_actions_download.py +21 -22
  41. dcoraid-1.2.0/dcoraid/gui/panel_downloads/widget_actions_download_ui.py +63 -0
  42. dcoraid-1.2.0/dcoraid/gui/panel_downloads/widget_download.py +149 -0
  43. dcoraid-1.2.0/dcoraid/gui/panel_downloads/widget_download_ui.py +67 -0
  44. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_find_data/widget_find_data.py +18 -19
  45. dcoraid-1.2.0/dcoraid/gui/panel_find_data/widget_find_data_ui.py +75 -0
  46. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_maintenance/widget_maintenance.py +11 -13
  47. dcoraid-1.2.0/dcoraid/gui/panel_maintenance/widget_maintenance_ui.py +75 -0
  48. dcoraid-1.2.0/dcoraid/gui/panel_my_data/dlg_share.py +120 -0
  49. dcoraid-1.2.0/dcoraid/gui/panel_my_data/dlg_share_ui.py +68 -0
  50. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_my_data/filter_chain_my_data.py +15 -15
  51. dcoraid-1.2.0/dcoraid/gui/panel_my_data/user_table.py +122 -0
  52. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_my_data/widget_my_data.py +20 -20
  53. dcoraid-1.2.0/dcoraid/gui/panel_my_data/widget_my_data_ui.py +65 -0
  54. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_uploads/dlg_upload.py +93 -97
  55. dcoraid-1.2.0/dcoraid/gui/panel_uploads/dlg_upload_ui.py +371 -0
  56. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_uploads/resources_model.py +1 -1
  57. dcoraid-1.2.0/dcoraid/gui/panel_uploads/upload_table.py +196 -0
  58. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_uploads/widget_actions_upload.py +27 -28
  59. dcoraid-1.2.0/dcoraid/gui/panel_uploads/widget_actions_upload_ui.py +63 -0
  60. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_uploads/widget_schema.py +2 -2
  61. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_uploads/widget_supplement_item.py +80 -81
  62. dcoraid-1.2.0/dcoraid/gui/panel_uploads/widget_supplement_item_ui.py +128 -0
  63. dcoraid-1.2.0/dcoraid/gui/panel_uploads/widget_supplement_tags_ui.py +72 -0
  64. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_uploads/widget_upload.py +28 -218
  65. dcoraid-1.2.0/dcoraid/gui/panel_uploads/widget_upload_ui.py +142 -0
  66. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/preferences/dlg_preferences.py +57 -61
  67. dcoraid-1.2.0/dcoraid/gui/preferences/dlg_preferences_ui.py +240 -0
  68. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/settings.py +2 -0
  69. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/status_widget.py +10 -2
  70. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/wizard_bagit/__init__.py +31 -32
  71. dcoraid-1.2.0/dcoraid/gui/wizard_bagit/wizard_bagit_ui.py +166 -0
  72. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/wizard_init/__init__.py +30 -50
  73. dcoraid-1.2.0/dcoraid/gui/wizard_init/wizard_init_ui.py +239 -0
  74. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/collect_icons.py +3 -3
  75. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/upload/job.py +18 -17
  76. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/upload/queue.py +5 -7
  77. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/upload/task.py +3 -4
  78. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/worker/daemon.py +4 -2
  79. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/worker/kthread.py +1 -1
  80. {dcoraid-1.1.2 → dcoraid-1.2.0/dcoraid.egg-info}/PKG-INFO +1 -2
  81. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid.egg-info/SOURCES.txt +55 -21
  82. dcoraid-1.2.0/dcoraid.egg-info/entry_points.txt +4 -0
  83. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid.egg-info/requires.txt +0 -1
  84. dcoraid-1.2.0/dcoraid.egg-info/scm_file_list.json +303 -0
  85. dcoraid-1.2.0/dcoraid.egg-info/scm_version.json +8 -0
  86. {dcoraid-1.1.2 → dcoraid-1.2.0}/pyproject.toml +23 -3
  87. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/common.py +116 -8
  88. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/conftest.py +4 -2
  89. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_api_base.py +22 -0
  90. dcoraid-1.2.0/tests/test_api_s3_retry.py +83 -0
  91. dcoraid-1.2.0/tests/test_api_users.py +54 -0
  92. dcoraid-1.2.0/tests/test_bagit_archive.py +211 -0
  93. dcoraid-1.2.0/tests/test_cli_archive_circle.py +140 -0
  94. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_cli_upload_task.py +43 -1
  95. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_dbmodel_api.py +84 -29
  96. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_dbmodel_api_cache.py +15 -20
  97. dcoraid-1.2.0/tests/test_dbmodel_api_lists.py +165 -0
  98. dcoraid-1.2.0/tests/test_dbmodel_api_search.py +89 -0
  99. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_dbmodel_extract.py +27 -0
  100. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_dbmodel_meta_cache.py +54 -0
  101. dcoraid-1.2.0/tests/test_dbmodel_meta_cache_datasets.py +36 -0
  102. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_gui.py +96 -65
  103. dcoraid-1.2.0/tests/test_gui_api.py +84 -0
  104. dcoraid-1.2.0/tests/test_gui_find_data.py +40 -0
  105. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_upload_task.py +1 -1
  106. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/panel_downloads/widget_download.ui +1 -1
  107. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/panel_my_data/dlg_share.ui +1 -1
  108. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/panel_uploads/widget_upload.ui +1 -1
  109. dcoraid-1.2.0/ui/run_uic.sh +18 -0
  110. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/wizard_init/wizard_init.ui +1 -1
  111. dcoraid-1.1.2/dcoraid/bagit/archive.py +0 -221
  112. dcoraid-1.1.2/dcoraid/gui/panel_my_data/dlg_share.py +0 -235
  113. dcoraid-1.1.2/dcoraid/gui/wizard_init/dcordevapi.ui +0 -190
  114. dcoraid-1.1.2/dcoraid.egg-info/entry_points.txt +0 -3
  115. dcoraid-1.1.2/tests/test_bagit_archive.py +0 -78
  116. {dcoraid-1.1.2 → dcoraid-1.2.0}/.github/workflows/deploy_github.yml +0 -0
  117. {dcoraid-1.1.2 → dcoraid-1.2.0}/.github/workflows/deploy_github_signed_windows.yml +0 -0
  118. {dcoraid-1.1.2 → dcoraid-1.2.0}/.github/workflows/deploy_pypi.yml +0 -0
  119. {dcoraid-1.1.2 → dcoraid-1.2.0}/LICENSE +0 -0
  120. {dcoraid-1.1.2 → dcoraid-1.2.0}/README.rst +0 -0
  121. {dcoraid-1.1.2 → dcoraid-1.2.0}/build-recipes/DCOR-Aid.icns +0 -0
  122. {dcoraid-1.1.2 → dcoraid-1.2.0}/build-recipes/DCOR-Aid.ico +0 -0
  123. {dcoraid-1.1.2 → dcoraid-1.2.0}/build-recipes/DCOR-AidLauncher.py +0 -0
  124. {dcoraid-1.1.2 → dcoraid-1.2.0}/build-recipes/Readme.md +0 -0
  125. {dcoraid-1.1.2 → dcoraid-1.2.0}/build-recipes/macos_DCOR-Aid.spec +0 -0
  126. {dcoraid-1.1.2 → dcoraid-1.2.0}/build-recipes/macos_build_app.sh +0 -0
  127. {dcoraid-1.1.2 → dcoraid-1.2.0}/build-recipes/macos_build_requirements.txt +0 -0
  128. {dcoraid-1.1.2 → dcoraid-1.2.0}/build-recipes/win_DCOR-Aid.spec +0 -0
  129. {dcoraid-1.1.2 → dcoraid-1.2.0}/build-recipes/win_build_requirements.txt +0 -0
  130. {dcoraid-1.1.2 → dcoraid-1.2.0}/build-recipes/win_dcoraid.iss_dummy +0 -0
  131. {dcoraid-1.1.2 → dcoraid-1.2.0}/build-recipes/win_make_iss.py +0 -0
  132. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/__init__.py +0 -0
  133. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/__main__.py +0 -0
  134. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/bagit/__init__.py +0 -0
  135. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/common.py +0 -0
  136. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/dbmodel/__init__.py +0 -0
  137. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/download/__init__.py +0 -0
  138. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/download/task.py +0 -0
  139. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/__init__.py +0 -0
  140. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/dbview/__init__.py +0 -0
  141. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/dbview/drag_table_widget.py +0 -0
  142. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/logs/__init__.py +0 -0
  143. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_downloads/__init__.py +0 -0
  144. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_find_data/__init__.py +0 -0
  145. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_maintenance/__init__.py +0 -0
  146. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_my_data/__init__.py +0 -0
  147. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_uploads/__init__.py +0 -0
  148. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_uploads/circle_mgr.py +0 -0
  149. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/panel_uploads/resource_schema_preset.py +0 -0
  150. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/preferences/__init__.py +0 -0
  151. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/tools/__init__.py +0 -0
  152. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/tools/wait_cursor.py +0 -0
  153. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/gui/updater.py +0 -0
  154. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/__init__.py +0 -0
  155. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/COPYING-ICONS +0 -0
  156. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/COPYING.LIB +0 -0
  157. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/README +0 -0
  158. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/16/application-exit.svg +0 -0
  159. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/16/dialog-cancel.svg +0 -0
  160. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/16/dialog-close.svg +0 -0
  161. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/16/dialog-messages.svg +0 -0
  162. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/16/dialog-ok-apply.svg +0 -0
  163. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/16/dialog-ok.svg +0 -0
  164. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/16/document-open-folder.svg +0 -0
  165. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/16/document-open.svg +0 -0
  166. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/16/document-save.svg +0 -0
  167. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/16/documentinfo.svg +0 -0
  168. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/16/edit-clear.svg +0 -0
  169. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/16/gtk-preferences.svg +0 -0
  170. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/16/messagebox_warning.svg +0 -0
  171. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/16/preferences-activities.svg +0 -0
  172. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/22/application-exit.svg +0 -0
  173. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/22/dialog-cancel.svg +0 -0
  174. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/22/dialog-close.svg +0 -0
  175. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/22/dialog-messages.svg +0 -0
  176. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/22/dialog-ok-apply.svg +0 -0
  177. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/22/dialog-ok.svg +0 -0
  178. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/22/document-open-folder.svg +0 -0
  179. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/22/document-open.svg +0 -0
  180. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/22/document-save.svg +0 -0
  181. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/22/documentinfo.svg +0 -0
  182. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/22/edit-clear.svg +0 -0
  183. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/22/gtk-preferences.svg +0 -0
  184. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/22/messagebox_warning.svg +0 -0
  185. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/22/preferences-activities.svg +0 -0
  186. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/24/application-exit.svg +0 -0
  187. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/24/dialog-cancel.svg +0 -0
  188. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/24/dialog-close.svg +0 -0
  189. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/24/dialog-messages.svg +0 -0
  190. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/24/dialog-ok-apply.svg +0 -0
  191. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/24/dialog-ok.svg +0 -0
  192. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/24/document-open-folder.svg +0 -0
  193. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/24/document-open.svg +0 -0
  194. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/24/document-save.svg +0 -0
  195. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/24/documentinfo.svg +0 -0
  196. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/24/edit-clear.svg +0 -0
  197. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/24/gtk-preferences.svg +0 -0
  198. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/24/messagebox_warning.svg +0 -0
  199. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/24/preferences-activities.svg +0 -0
  200. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/32/application-exit.svg +0 -0
  201. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/32/dialog-cancel.svg +0 -0
  202. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/32/dialog-messages.svg +0 -0
  203. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/32/dialog-ok-apply.svg +0 -0
  204. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/32/dialog-ok.svg +0 -0
  205. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/32/document-open-folder.svg +0 -0
  206. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/32/document-open.svg +0 -0
  207. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/32/document-save.svg +0 -0
  208. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/actions/32/gtk-preferences.svg +0 -0
  209. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/16/dialog-error.svg +0 -0
  210. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/16/dialog-information.svg +0 -0
  211. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/16/dialog-question.svg +0 -0
  212. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/16/dialog-warning.svg +0 -0
  213. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/22/dialog-error.svg +0 -0
  214. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/22/dialog-information.svg +0 -0
  215. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/22/dialog-question.svg +0 -0
  216. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/22/dialog-warning.svg +0 -0
  217. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/24/dialog-error.svg +0 -0
  218. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/24/dialog-information.svg +0 -0
  219. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/24/dialog-question.svg +0 -0
  220. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/24/dialog-warning.svg +0 -0
  221. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/32/dialog-warning.svg +0 -0
  222. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/48/dialog-warning.svg +0 -0
  223. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/64/dialog-error.svg +0 -0
  224. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/64/dialog-information.svg +0 -0
  225. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/64/dialog-question.svg +0 -0
  226. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/breeze/status/64/dialog-warning.svg +0 -0
  227. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/dcoraid/dcor.png +0 -0
  228. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/LICENSE.txt +0 -0
  229. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/angle-down.svg +0 -0
  230. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/angles-down.svg +0 -0
  231. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/ban.svg +0 -0
  232. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/book.svg +0 -0
  233. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/broom.svg +0 -0
  234. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/check-double.svg +0 -0
  235. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/child.svg +0 -0
  236. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/circle.svg +0 -0
  237. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/clinic-medical.svg +0 -0
  238. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/code-branch.svg +0 -0
  239. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/cogs.svg +0 -0
  240. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/download.svg +0 -0
  241. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/exclamation-triangle.svg +0 -0
  242. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/eye.svg +0 -0
  243. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/filter.svg +0 -0
  244. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/folder-tree.svg +0 -0
  245. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/folder.svg +0 -0
  246. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/globe.svg +0 -0
  247. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/hat-wizard.svg +0 -0
  248. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/hourglass.svg +0 -0
  249. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/info.svg +0 -0
  250. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/magnifying-glass.svg +0 -0
  251. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/pencil.svg +0 -0
  252. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/people-arrows.svg +0 -0
  253. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/people-carry.svg +0 -0
  254. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/puzzle-piece.svg +0 -0
  255. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/redo.svg +0 -0
  256. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/scroll.svg +0 -0
  257. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/share-nodes.svg +0 -0
  258. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/shipping-fast.svg +0 -0
  259. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/slash.svg +0 -0
  260. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/street-view.svg +0 -0
  261. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/tag.svg +0 -0
  262. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/trash-alt.svg +0 -0
  263. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/trash-can-arrow-up.svg +0 -0
  264. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/trash.svg +0 -0
  265. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/undo.svg +0 -0
  266. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/upload.svg +0 -0
  267. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/user-lock.svg +0 -0
  268. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/user-times.svg +0 -0
  269. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/user.svg +0 -0
  270. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/fontawesome/solid/users.svg +0 -0
  271. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon-theme/index.theme +0 -0
  272. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/icon.png +0 -0
  273. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/img/splash.png +0 -0
  274. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/loggers.py +0 -0
  275. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/upload/__init__.py +0 -0
  276. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid/worker/__init__.py +0 -0
  277. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid.egg-info/dependency_links.txt +0 -0
  278. {dcoraid-1.1.2 → dcoraid-1.2.0}/dcoraid.egg-info/top_level.txt +0 -0
  279. {dcoraid-1.1.2 → dcoraid-1.2.0}/docs/artwork/Readme.md +0 -0
  280. {dcoraid-1.1.2 → dcoraid-1.2.0}/docs/artwork/dcoraid_icon.svg +0 -0
  281. {dcoraid-1.1.2 → dcoraid-1.2.0}/docs/artwork/dcoraid_splash.svg +0 -0
  282. {dcoraid-1.1.2 → dcoraid-1.2.0}/docs/notes/software_design.svg +0 -0
  283. {dcoraid-1.1.2 → dcoraid-1.2.0}/setup.cfg +0 -0
  284. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/.gitignore +0 -0
  285. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/__init__.py +0 -0
  286. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/data/calibration_beads_47.rtdc +0 -0
  287. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/data/calibration_beads_47_nocomp.rtdc +0 -0
  288. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/data/text_file.txt +0 -0
  289. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/requirements.txt +0 -0
  290. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_api_dataset.py +0 -0
  291. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_api_s3.py +0 -0
  292. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_bagit_info.py +0 -0
  293. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_bagit_manifest.py +0 -0
  294. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_common.py +0 -0
  295. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_dbmodel_meta_cache_sqlite.py +0 -0
  296. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_download_job.py +0 -0
  297. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_download_queue.py +0 -0
  298. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_download_task.py +0 -0
  299. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_gui_updater.py +0 -0
  300. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_upload_job.py +0 -0
  301. {dcoraid-1.1.2 → dcoraid-1.2.0}/tests/test_upload_queue.py +0 -0
  302. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/dbview/filter_base.ui +0 -0
  303. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/dbview/filter_chain.ui +0 -0
  304. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/logs/widget_log.ui +0 -0
  305. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/main.ui +0 -0
  306. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/panel_downloads/widget_actions_download.ui +0 -0
  307. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/panel_find_data/widget_find_data.ui +0 -0
  308. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/panel_maintenance/widget_maintenance.ui +0 -0
  309. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/panel_my_data/widget_my_data.ui +0 -0
  310. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/panel_uploads/dlg_upload.ui +0 -0
  311. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/panel_uploads/widget_actions_upload.ui +0 -0
  312. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/panel_uploads/widget_supplement_item.ui +0 -0
  313. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/panel_uploads/widget_supplement_tags.ui +0 -0
  314. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/preferences/dlg_preferences.ui +0 -0
  315. {dcoraid-1.1.2/dcoraid/gui → dcoraid-1.2.0/ui}/wizard_bagit/wizard_bagit.ui +0 -0
@@ -57,7 +57,7 @@ jobs:
57
57
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
58
58
  DCOR_API_KEY: ${{ secrets.DCOR_API_KEY }}
59
59
  run: |
60
- coverage run --source=dcoraid -m pytest tests
60
+ coverage run --source=dcoraid -m pytest --durations=20 tests
61
61
  - name: Lint with flake8
62
62
  run: |
63
63
  flake8 --exclude _version.py .
@@ -111,6 +111,7 @@ pytest-failures.txt
111
111
  _version.py
112
112
 
113
113
  _dev_*.py
114
+ _dev_*
114
115
 
115
116
  # QtCreator temporary files
116
117
  *.ui.autosave
@@ -118,3 +119,7 @@ _dev_*.py
118
119
 
119
120
  # line_profiler
120
121
  *.lprof
122
+
123
+ # API keys of (auto-generated) test users
124
+ tests/api_key
125
+ tests/api_key_2
@@ -1,3 +1,87 @@
1
+ 1.2.0
2
+ - feat: new CLI `dcoraid-archive-circle` for archiving an entire circle
3
+ in the BagIt format
4
+ - fix: `dcoraid-upload-task` ignored the `--cache_dir` argument
5
+ - fix: `dcoraid-upload-task` SERVER argument had a non-functional default
6
+ - fix: cleanup `dcoraid-upload-task` with ruff
7
+ - fix: `dcoraid-upload-task` silenced exceptions by returning in `finally`
8
+ - fix: BagIt archiving silently created bags for resources whose download
9
+ or verification failed
10
+ - fix: BagIt archives of recently uploaded datasets could be invalid,
11
+ because the condensed file or the SHA256 sum of a resource was missing
12
+ in the manifest
13
+ - fix: datasets inserted in bulk into the local database were not
14
+ registered with their circle, so updating the database again in the
15
+ same session failed or created duplicate entries
16
+ - fix: comparing the cached dataset list with a list of a different
17
+ length returned True or raised an IndexError
18
+ - fix: dataset search via the API returned more results than `limit`
19
+ for limits above 1000 and requested more than 1000 rows at once
20
+ - fix: dataset search via the API looped forever when datasets were
21
+ deleted in-between paginated requests
22
+ - fix: only the first 25 circles and collections on a server were
23
+ considered for the local database and for shared datasets (CKAN
24
+ limits `all_fields` list results to 25 items)
25
+ - fix: datasets shared with the user were searched in all circles
26
+ and collections instead of only those the user is a member of
27
+ - fix: "Find data" tab failed to fetch the site title when the server
28
+ was stored without scheme (e.g. in the playground)
29
+ - fix: retry twice on read errors
30
+ - enh: automatically create a new DCOR-dev user in the playground user
31
+ scenario if the previous user was purged
32
+ - enh: `dcoraid-upload-task` reads the API key from the `DCOR_API_KEY`
33
+ environment variable if it is not passed as an argument
34
+ - enh: retry downloading resources and archiving datasets in BagIt
35
+ archiving when transient errors occur
36
+ - enh: new methods `get_circles_user` and `get_collections_user`
37
+ for database interrogators
38
+ - enh: `get_users` of database interrogators accepts `query` and `limit`
39
+ (only up to `limit` users are returned, by default 20)
40
+ - enh: wait between retries of failed S3 upload requests (5 s, doubling
41
+ with each retry) instead of retrying immediately
42
+ - enh: use the API action `dcor_group_list_for_user` (ckanext-dcor_schemas
43
+ 1.2.0) for listing the collections of a user, with a fallback for
44
+ older servers (#108)
45
+ - enh: do not retry API requests when the server name cannot be
46
+ resolved (DCOR-Aid froze for minutes on Windows with a wrong server)
47
+ - ref: remove the fallback to uploading resources via CKAN
48
+ (`resource_add_upload_legacy_indirect_ckan`); DCOR only accepts
49
+ uploads to S3
50
+ - ref: convert `dcoraid.cli` module into a package; the upload task CLI
51
+ now lives in `dcoraid.cli.task_upload_task`
52
+ - ref: use `super()`, PEP 604 optional type hints, and builtin/
53
+ `collections.abc` types instead of deprecated `typing` imports
54
+ - ref: use `from __future__ import annotations` in all modules with
55
+ type annotations and `Callable` instead of `callable` in type hints
56
+ - ref: fix various ruff findings
57
+ - ref: `search_dataset` now accepts `query` instead of `text` globally
58
+ - ref: correct return type hint of `meta_cache._values_only`
59
+ - ref: `DBExtract.__add__` accepts lists like `DBExtract.__iadd__`
60
+ - ref: correct type hints for `CKANAPI.get` kwargs and CLI exit status
61
+ - ref: migrate GUI design to the one of DCscope: all .ui files are in
62
+ the top-level `ui` directory and converted with `ui/run_uic.sh` to
63
+ `*_ui.py` modules, which are used via `self.ui`
64
+ - ref: move `UploadTableWidget`, `DownloadTableWidget`, and
65
+ `UserTableWidget` to separate modules
66
+ - ref: fix remaining ruff findings and add missing type annotations
67
+ - ref: new `dcoraid.api.create_dcor_dev_user` and
68
+ `dcoraid.api.is_valid_api_key` used by the setup wizard and the tests
69
+ - setup: add tool.ruff.lint section to pyproject.toml
70
+ - setup: remove `requests_toolbelt` dependency (was only used for
71
+ uploads via CKAN)
72
+ - tests: automatically create test users on DCOR-dev if `DCOR_API_KEY`
73
+ is not set or `tests/api_key` is missing or invalid
74
+ - tests: replace tests that required access to the `figshare-import`
75
+ circle with a circle, collection, and dataset shared by a second
76
+ automatically created test user
77
+ - tests: clear persistent upload jobs before and after each GUI test,
78
+ so that new main windows do not upload jobs of previous tests again
79
+ - tests: let name resolution fail immediately in
80
+ `test_gui_start_with_bad_server` (took 20 minutes on Windows)
81
+ 1.1.3
82
+ - enh: use `HTTPAdapter` with `urlib3`'s `Retry` to handle connection issues
83
+ - enh: use `requests.Session` when caching is disabled
84
+ - enh: print UTC time in error messages to make debugging easier
1
85
  1.1.2
2
86
  - fix: archiving to BagIt format circle selection did not include all circles
3
87
  1.1.1
@@ -574,7 +658,7 @@
574
658
  - fix: start even if DCOR is down
575
659
  - fix: do not display mean upload rate during upload
576
660
  - fix: require restart when server is changed
577
- - enh: ask user to create a Circle when the user is not member of any
661
+ - enh: ask user to create a Circle when the user is not member of any
578
662
  - enh: add basic wizard functionalities
579
663
  - enh: resource upload supports supplementary resource schemas
580
664
  - enh: stick to package_revise when finalizing dataset
@@ -3,4 +3,4 @@ include LICENSE
3
3
  include README.rst
4
4
  recursive-include examples *.py
5
5
  recursive-include tests *.py
6
- recursive-include dcoraid *.css *.png *.py *.svg *.theme *.txt *.ui COPYING* LICENSE README
6
+ recursive-include dcoraid *.css *.png *.py *.svg *.theme *.txt COPYING* LICENSE README
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dcoraid
3
- Version: 1.1.2
3
+ Version: 1.2.0
4
4
  Summary: GUI/CLI for managing data on DCOR
5
5
  Author: Paul Müller, Eoghan O'Connell, Raghava Alajangi
6
6
  Maintainer-email: Paul Müller <dev@craban.de>
@@ -21,7 +21,6 @@ Requires-Dist: numpy>=1.21
21
21
  Requires-Dist: requests>=2.31
22
22
  Requires-Dist: urllib3>=2.0
23
23
  Requires-Dist: requests_cache
24
- Requires-Dist: requests_toolbelt>=1.0.0
25
24
  Provides-Extra: gui
26
25
  Requires-Dist: PyQt6; extra == "gui"
27
26
  Dynamic: license-file
@@ -17,4 +17,4 @@ datas += collect_data_files("dcoraid", subdir="img")
17
17
  # Add the Zstandard library used by dclab
18
18
  datas += collect_data_files("hdf5plugin", includes=["plugins/libh5zstd.*"])
19
19
 
20
- hiddenimports = ["dclab.cli", "requests_toolbelt"]
20
+ hiddenimports = ["dclab.cli"]
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.1.2'
22
- __version_tuple__ = version_tuple = (1, 1, 2)
21
+ __version__ = version = '1.2.0'
22
+ __version_tuple__ = version_tuple = (1, 2, 0)
23
23
 
24
- __commit_id__ = commit_id = 'g221c2c8a3'
24
+ __commit_id__ = commit_id = 'g45bf2a16c'
@@ -3,6 +3,7 @@ from .ckan_api import CKANAPI
3
3
  from .dataset import (dataset_create, dataset_activate, dataset_draft_remove,
4
4
  dataset_draft_remove_all)
5
5
  from .dataset import resource_add, resource_exists
6
+ from .users import create_dcor_dev_user, is_valid_api_key
6
7
  from .errors import (APIError, APIBadGatewayError, APIConflictError,
7
8
  APIAuthorizationError, NoAPIKeyError, APINotFoundError,
8
9
  APIGatewayTimeoutError, APIOutdatedError)
@@ -4,15 +4,16 @@ import copy
4
4
  import functools
5
5
  import json
6
6
  import logging
7
- import numbers
8
7
  import pathlib
9
8
  import random
10
9
  import traceback
11
10
  import urllib.parse
12
-
11
+ from urllib3.exceptions import MaxRetryError, NameResolutionError
12
+ from urllib3.util.retry import Retry
13
13
 
14
14
  from dclab.external.packaging import parse as parse_version
15
15
  import requests
16
+ from requests.adapters import HTTPAdapter
16
17
  import requests_cache
17
18
 
18
19
  from .._version import version
@@ -26,13 +27,44 @@ from .errors import (APIConflictError, APINotFoundError, NoAPIKeyError,
26
27
  MIN_CKAN_VERSION = "2.9.4"
27
28
 
28
29
  #: List of license lists for each DCOR server
29
- SERVER_LICENCES = {}
30
+ SERVER_LICENCES: dict[str, list[dict]] = {}
30
31
 
31
32
  #: List of supplementary resource schema dictionaries
32
- SERVER_RSS = {}
33
+ SERVER_RSS: dict[str, dict] = {}
33
34
 
34
35
  #: List of supported resource suffixes
35
- SERVER_RSUFFIX = {}
36
+ SERVER_RSUFFIX: dict[str, list[str]] = {}
37
+
38
+
39
+ class RetryExceptNameResolution(Retry):
40
+ """Retry strategy that gives up immediately if a host cannot be resolved
41
+
42
+ A failed name lookup is rarely transient (e.g. a typo in the server
43
+ name), and on Windows every failed lookup may take half a minute.
44
+ """
45
+ def increment(self, method=None, url=None, response=None, error=None,
46
+ _pool=None, _stacktrace=None):
47
+ if isinstance(error, NameResolutionError):
48
+ raise MaxRetryError(_pool, url, error) from error
49
+ return super().increment(method=method,
50
+ url=url,
51
+ response=response,
52
+ error=error,
53
+ _pool=_pool,
54
+ _stacktrace=_stacktrace)
55
+
56
+
57
+ # Configure retry strategy
58
+ retry_strategy = RetryExceptNameResolution(
59
+ total=3, # Total retries
60
+ backoff_factor=1.5, # {backoff factor} * (2 ** ({number prev. retries}))
61
+ connect=3, # Handle connection errors (e.g. because the server is busy)
62
+ read=2, # Retry twice on read errors (e.g. DCOR closed connection)
63
+ redirect=False, # implies raise_on_redirect=False
64
+ status=0, # Fail on bad status codes
65
+ allowed_methods=["HEAD", "GET", "OPTIONS"] # Retry only for safe methods
66
+ )
67
+ adapter = HTTPAdapter(max_retries=retry_strategy)
36
68
 
37
69
 
38
70
  class CKANAPI:
@@ -107,7 +139,10 @@ class CKANAPI:
107
139
  },
108
140
  **kwargs)
109
141
  else:
110
- self.req_ses = requests
142
+ self.req_ses = requests.Session()
143
+
144
+ self.req_ses.mount("https://", adapter)
145
+ self.req_ses.mount("http://", adapter)
111
146
 
112
147
  def __repr__(self):
113
148
  return f"<CKANAPI {self.api_url} at {hex(id(self))}>"
@@ -292,7 +327,7 @@ class CKANAPI:
292
327
  def get(self,
293
328
  api_call: str,
294
329
  timeout: float = 27.9,
295
- **kwargs: str | numbers.Number):
330
+ **kwargs: str | float):
296
331
  """GET request
297
332
 
298
333
  Parameters
@@ -316,8 +351,8 @@ class CKANAPI:
316
351
  if kwargs:
317
352
  # Add keyword arguments
318
353
  kwv = []
319
- for kw in kwargs:
320
- kwv.append(f"{kw}={kwargs[kw]}")
354
+ for kw, val in kwargs.items():
355
+ kwv.append(f"{kw}={val}")
321
356
  api_call += "?" + "&".join(kwv)
322
357
 
323
358
  req = self.req_ses.get(self.api_url + api_call,
@@ -400,7 +435,7 @@ class CKANAPI:
400
435
  ----------
401
436
  api_call: str
402
437
  An API call function (e.g. "package_create")
403
- data: dict, MultipartEncoder, ...
438
+ data: dict
404
439
  The data connected to the post request. For
405
440
  "package_create", this would be a dictionary
406
441
  with the dataset name, author, license, etc.
@@ -444,7 +479,7 @@ class CKANAPI:
444
479
 
445
480
  def require_collection(self,
446
481
  name: str,
447
- title: str = None,
482
+ title: str | None = None,
448
483
  exist_ok: bool = True,
449
484
  ):
450
485
  """Return a collection dict with the given name and optional title
@@ -464,7 +499,7 @@ class CKANAPI:
464
499
  rand = ""
465
500
  title = (title or name).strip()
466
501
  # create the collection
467
- for ii in range(10):
502
+ for _ in range(10):
468
503
  try:
469
504
  col_dict = self.post("group_create",
470
505
  {"title": title,
@@ -1,15 +1,11 @@
1
1
  """Convenience wrappers for commonly used API calls"""
2
+ from __future__ import annotations
3
+
2
4
  import copy
3
5
  import functools
4
6
  import logging
5
7
  import pathlib
6
- import time
7
- from typing import Callable, Dict
8
-
9
- import requests
10
- from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor
11
-
12
- from ..common import ConnectionTimeoutErrors
8
+ from collections.abc import Callable
13
9
 
14
10
  from .errors import (
15
11
  APIBadRequest, APIConflictError, APINotFoundError, NoS3UploadAvailableError
@@ -33,22 +29,18 @@ def dataset_activate(dataset_id: str, api: CKANAPI):
33
29
  api: dcoraid.api.CKANAPI
34
30
  API instance with server, api_key (and optional certificate)
35
31
  """
36
- try:
37
- # First check whether the dataset is already active. Use a long
38
- # timeout for this critical step.
39
- ds_dict = api.get("package_show", id=dataset_id, timeout=500)
40
- except ConnectionTimeoutErrors:
41
- raise
42
- else:
43
- if not ds_dict["state"] == "active":
44
- revise_dict = {
45
- "match": {"id": dataset_id},
46
- "update": {"state": "active"}}
47
- api.post("package_revise",
48
- revise_dict,
49
- # Dataset activation may take long when there
50
- # are a lot of resources.
51
- timeout=500)
32
+ # First check whether the dataset is already active. Use a long
33
+ # timeout for this critical step.
34
+ ds_dict = api.get("package_show", id=dataset_id, timeout=500)
35
+ if ds_dict["state"] != "active":
36
+ revise_dict = {
37
+ "match": {"id": dataset_id},
38
+ "update": {"state": "active"}}
39
+ api.post("package_revise",
40
+ revise_dict,
41
+ # Dataset activation may take long when there
42
+ # are a lot of resources.
43
+ timeout=500)
52
44
 
53
45
 
54
46
  def dataset_create(dataset_dict, api, resources=None,
@@ -202,16 +194,18 @@ def resource_add(dataset_id, path, api, resource_name=None,
202
194
  it, only update the resource_dict.
203
195
  monitor_callback: None or callable
204
196
  This callable is used to monitor the upload progress. It must
205
- accept one argument: a
206
- `requests_toolbelt.MultipartEncoderMonitor` object.
197
+ accept one argument: an object with the attributes `len` (file
198
+ size) and `bytes_read` (number of bytes uploaded).
207
199
 
208
200
  Returns
209
201
  -------
210
202
  srv_time: float
211
- Total time the server (nginx+uwsgi) needed to process the upload
203
+ Always 0 (kept for backwards compatibility; this was the time the
204
+ server needed to process uploads via CKAN, which are not supported
205
+ anymore)
212
206
  etag: str
213
- ETag of the resource on S3; returns None if the upload was not
214
- performed via S3
207
+ ETag of the resource on S3; returns None if the resource already
208
+ existed and was not uploaded
215
209
 
216
210
  See Also
217
211
  --------
@@ -234,31 +228,16 @@ def resource_add(dataset_id, path, api, resource_name=None,
234
228
  resource_name=resource_name,
235
229
  api=api):
236
230
 
237
- # Perform the upload
238
- try:
239
- # Uploading directly to S3 is the preferred option.
240
- etag = resource_add_upload_direct_s3(
241
- api=api,
242
- resource_path=path,
243
- dataset_id=dataset_id,
244
- resource_name=resource_name,
245
- monitor_callback=monitor_callback,
246
- logger=logger,
247
- timeout=27.9
248
- )
249
- except NoS3UploadAvailableError as e:
250
- # This is the fall-back option that causes a performance hit
251
- # on the DCOR server and will be unsupported in the future.
252
- logger.warning(str(e))
253
- resource_add_upload_legacy_indirect_ckan(
254
- api=api,
255
- resource_path=path,
256
- dataset_id=dataset_id,
257
- resource_name=resource_name,
258
- monitor_callback=monitor_callback,
259
- logger=logger,
260
- timeout=500,
261
- )
231
+ # Perform the upload (DCOR only accepts uploads to S3)
232
+ etag = resource_add_upload_direct_s3(
233
+ api=api,
234
+ resource_path=path,
235
+ dataset_id=dataset_id,
236
+ resource_name=resource_name,
237
+ monitor_callback=monitor_callback,
238
+ logger=logger,
239
+ timeout=27.9
240
+ )
262
241
 
263
242
  # If we are here, then the upload was successful
264
243
  logger.info(f"Finished upload of {dataset_id}/{resource_name}")
@@ -282,8 +261,8 @@ def resource_add_upload_direct_s3(
282
261
  dataset_id: str,
283
262
  resource_path: str | pathlib.Path,
284
263
  resource_name: str,
285
- monitor_callback: Callable = None,
286
- logger: logging.Logger = None,
264
+ monitor_callback: Callable | None = None,
265
+ logger: logging.Logger | None = None,
287
266
  timeout: float = 27.9) -> str:
288
267
  """Direct resource upload to S3
289
268
 
@@ -335,9 +314,10 @@ def resource_add_upload_direct_s3(
335
314
  raise NoS3UploadAvailableError(f"Server {api.server} does not yet "
336
315
  f"support direct upload to S3")
337
316
 
338
- logger.info(
339
- f"We have {len(upload_info['upload_urls'])} upload parts for "
340
- f"a resource of size {file_size/1024**2:.2f} MiB of {upload_id}")
317
+ if logger is not None:
318
+ logger.info(
319
+ f"We have {len(upload_info['upload_urls'])} upload parts for "
320
+ f"a resource of size {file_size/1024**2:.2f} MiB of {upload_id}")
341
321
 
342
322
  etag = upload_s3_presigned(
343
323
  path=resource_path,
@@ -361,82 +341,11 @@ def resource_add_upload_direct_s3(
361
341
  return etag
362
342
 
363
343
 
364
- def resource_add_upload_legacy_indirect_ckan(
365
- api: CKANAPI,
366
- resource_path: str | pathlib.Path,
367
- dataset_id: str,
368
- resource_name: str,
369
- monitor_callback: Callable = None,
370
- logger: logging.Logger = None,
371
- timeout: float = 27.9):
372
- """Legacy upload procedure for resources
373
-
374
- This method uses the legacy path (nginx -> uwsgi -> CKAN API) to
375
- upload resources to the block storage of the DCOR instance. It is
376
- termed legacy, because it is very inefficient to process resources
377
- like this. A better approach is to upload resources directly to S3.
378
- DCOR supports uploading resources directly to S3, which is facilitated
379
- via the `resource_upload_s3_url` API action providing a presigned
380
- upload link. (see :func:`resource_add_upload_direct_s3`)
381
- """
382
- upload_id = f"{dataset_id}/{resource_name}"
383
- if logger is not None:
384
- logger.info(f"Commencing legacy upload of {upload_id}")
385
-
386
- with pathlib.Path(resource_path).open("rb") as fd:
387
- # use package_revise to upload the resource
388
- # https://github.com/DCOR-dev/DCOR-Aid/issues/28
389
- e = MultipartEncoder(fields={
390
- 'match__id': dataset_id,
391
- 'update__resources__extend': f'[{{"name":"{resource_name}"}}]',
392
- 'update__resources__-1__upload': (resource_name, fd)})
393
- m = MultipartEncoderMonitor(e, monitor_callback)
394
- # Increase the read size to speed-up upload (the default chunk
395
- # size for uploads in urllib is 8k which results in a lot of
396
- # Python code being involved in uploading a 20GB file; Setting
397
- # the chunk size to 4MB should increase the upload speed):
398
- # https://github.com/requests/toolbelt/issues/75
399
- # #issuecomment-237189952
400
- m._read = m.read
401
- m.read = lambda size: m._read(4 * 1024 * 1024)
402
-
403
- try:
404
- api.post("package_revise",
405
- data=m,
406
- dump_json=False,
407
- headers={"Content-Type": m.content_type},
408
- timeout=timeout)
409
- except requests.exceptions.Timeout:
410
- # This means that the server does not respond. This is ok,
411
- # because we can just check whether the resource was
412
- # processed correctly.
413
- if logger is not None:
414
- logger.info(f"Timeout for upload {upload_id}")
415
- start_wait_srv = time.monotonic()
416
- wait_time_minutes = 60
417
- for ii in range(wait_time_minutes):
418
- if resource_exists(dataset_id=dataset_id,
419
- resource_name=resource_name,
420
- api=api):
421
- srv_time = timeout + time.monotonic() - start_wait_srv
422
- if logger is not None:
423
- logger.info(
424
- f"Waited {srv_time / 60:.1f} min for {upload_id}")
425
- break
426
- else:
427
- if logger is not None:
428
- logger.info(f"Waiting {ii + 1} min for {upload_id}")
429
- time.sleep(60)
430
- else:
431
- raise ValueError(f"Timeout or {upload_id} not processed after "
432
- + f"{wait_time_minutes} minutes!")
433
-
434
-
435
344
  def resource_exists(dataset_id: str,
436
345
  resource_name: str,
437
346
  api: CKANAPI,
438
- check_resource_dict: Dict = None,
439
- dataset_dict: Dict = None):
347
+ check_resource_dict: dict | None = None,
348
+ dataset_dict: dict | None = None):
440
349
  """Check whether a resource exists in a dataset on DCOR
441
350
 
442
351
  Parameters
@@ -463,6 +372,7 @@ def resource_exists(dataset_id: str,
463
372
  if dataset_dict is None:
464
373
  dataset_dict = api.get("package_show", id=dataset_id,
465
374
  timeout=500)
375
+ assert dataset_dict is not None
466
376
  for resource in dataset_dict.get("resources", []):
467
377
  if resource["name"] == resource_name:
468
378
  # check that the resource dict matches
@@ -473,5 +383,4 @@ def resource_exists(dataset_id: str,
473
383
  # or it is wrong.
474
384
  return False
475
385
  return True
476
- else:
477
- return False
386
+ return False
@@ -1,58 +1,46 @@
1
1
  class APIError(BaseException):
2
2
  """General CKANAPI error"""
3
- pass
4
3
 
5
4
 
6
5
  class APIAuthorizationError(APIError):
7
6
  """User credentials are invalid"""
8
- pass
9
7
 
10
8
 
11
9
  class APIBadGatewayError(APIError):
12
10
  """Cannot connect to DCOR server"""
13
- pass
14
11
 
15
12
 
16
13
  class APIBadRequest(APIError):
17
14
  """An API command cannot be found"""
18
- pass
19
15
 
20
16
 
21
17
  class APIConflictError(APIError):
22
18
  """Invalid payload to DCOR server"""
23
- pass
24
19
 
25
20
 
26
21
  class APIGatewayTimeoutError(APIError):
27
22
  """Timeout due to network connection"""
28
- pass
29
23
 
30
24
 
31
25
  class APINotFoundError(APIError):
32
26
  """Requested object not found on DCOR"""
33
- pass
34
27
 
35
28
 
36
29
  class APIOutdatedError(APIError):
37
30
  """DCOR-Aid is outdated, the server requests a newer version"""
38
- pass
39
31
 
40
32
 
41
33
  class NoAPIKeyError(APIError):
42
34
  """DCOR does not have an API token"""
43
- pass
44
35
 
45
36
 
46
37
  class NoS3UploadAvailableError(BaseException):
47
38
  """Used for identifying DCOR servers that don't support direct S3 upload"""
48
- pass
49
39
 
50
40
 
51
41
  class S3UploadError(BaseException):
52
42
  """Raised when an upload to S3 failed"""
53
- pass
54
43
 
55
44
 
56
45
  class ServerUnderMaintenanceError(BaseException):
57
46
  """Raised when the DCOR server is under maintenance"""
58
- pass