diff --git a/miqa/core/rest/project.py b/miqa/core/rest/project.py index 492617c5..23949ec6 100644 --- a/miqa/core/rest/project.py +++ b/miqa/core/rest/project.py @@ -80,20 +80,19 @@ def get_my_project_role(self, obj): return obj.get_user_role(self.context['user']) def get_scan_states(self, obj): - def convert_state_string(last_reviewer_role): - if last_reviewer_role == 'tier_2_reviewer': + def convert_state_string(last_decision): + last_reviewer_role = obj.get_user_role(last_decision.creator) + if last_reviewer_role == 'tier_2_reviewer' or last_decision.decision == 'U': + # scan is complete if it is marked usable by anyone + # or if marked at all by a tier 2 reviewer return 'complete' elif last_reviewer_role == 'tier_1_reviewer': return 'needs tier 2 review' - else: - return last_reviewer_role return { - str(scan.id): convert_state_string( - obj.get_user_role(scan.decisions.latest('created').creator) - if scan.decisions.count() > 0 and scan.decisions.latest('created').creator - else 'unreviewed' - ) + str(scan.id): convert_state_string(scan.decisions.latest('created')) + if scan.decisions.count() > 0 and scan.decisions.latest('created').creator + else 'unreviewed' for exp in obj.experiments.all() for scan in exp.scans.all() } diff --git a/miqa/core/tests/pyppeteer/test_save_decisions.py b/miqa/core/tests/pyppeteer/test_save_decisions.py index 84baac23..b1cffa3b 100644 --- a/miqa/core/tests/pyppeteer/test_save_decisions.py +++ b/miqa/core/tests/pyppeteer/test_save_decisions.py @@ -107,10 +107,11 @@ async def test_save_decisions_tier_1( ).click() await page.waitFor(3_000) - # confirm that the number of scans awaiting tier 2 review is 3 + # confirm that the number of scans awaiting tier 2 review is 1; + # only the second scan does not have "Usable" as the latest decision complete_span = await (page.waitForXPath('//span[contains(., "tier 2 review (")]')) complete_text = (await page.evaluate('(element) => element.textContent', complete_span)).strip() - assert complete_text == 'needs tier 2 review (3)' + assert complete_text == 'needs tier 2 review (1)' @pytest.mark.pyppeteer diff --git a/web_client/package-lock.json b/web_client/package-lock.json index f7f0b8cd..958d0e05 100644 --- a/web_client/package-lock.json +++ b/web_client/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@girder/oauth-client": "^0.7.7", "@mdi/font": "^6.5.95", - "@sentry/vue": "^6.17.6", + "@sentry/vue": "^7.9.0", "@types/idle-js": "^1.2.1", "@types/jest": "^27.0.1", "@typescript-eslint/eslint-plugin": "^4.33.0", @@ -2084,98 +2084,82 @@ } }, "node_modules/@sentry/browser": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.18.0.tgz", - "integrity": "sha512-fQZdFs0jnBijVmhDWNIsHD0rGsIXup/1UYgMZqdBYYSBBQ7Ffpw+6nX1/vyKsCpLdgYhbi88eK/XO+QHtUJE2w==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.9.0.tgz", + "integrity": "sha512-R0/EatdSBPZ+orsD5Mu/Gq8XmEfr/KCzJv05S35GVPDkIgczIJ2AYlHgchnEO0m63jDFyWLzUteQmPZ3pao9PQ==", "dependencies": { - "@sentry/core": "6.18.0", - "@sentry/types": "6.18.0", - "@sentry/utils": "6.18.0", + "@sentry/core": "7.9.0", + "@sentry/types": "7.9.0", + "@sentry/utils": "7.9.0", "tslib": "^1.9.3" }, "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/@sentry/core": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.18.0.tgz", - "integrity": "sha512-I3iQVfMWHXR/LtevJg83aD7UAiUBLz1xAW8y3gd5lJej96UNv/4TbCmKZumYnEJMXf8EcFlg8t48W0Bl1GxhEg==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.9.0.tgz", + "integrity": "sha512-WVGd2hV7Clcpl7/GL8LsRr4Zk9o/7o4rZHfs1Qed5lMRNYcxiMwucC1CYILVpJqVfY+8vIRP9v9Ss9ta2VUikw==", "dependencies": { - "@sentry/hub": "6.18.0", - "@sentry/minimal": "6.18.0", - "@sentry/types": "6.18.0", - "@sentry/utils": "6.18.0", + "@sentry/hub": "7.9.0", + "@sentry/types": "7.9.0", + "@sentry/utils": "7.9.0", "tslib": "^1.9.3" }, "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/@sentry/hub": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.18.0.tgz", - "integrity": "sha512-E2GrrNcidyT67ONU3btHO5vyS1bPQNdWqC09sUc1F3q/nQyvc7L2W09TKY2veaMZQtC9EU760fTG1hMmgGwPmw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-7.9.0.tgz", + "integrity": "sha512-KzPbGCB5mONgsXEzqHy6uOaOuqLnhmFmSpGg+M03J6UJnJaNM7nrNp80MhStmjLMq6whEYVE07DrMAn3+iaQdg==", "dependencies": { - "@sentry/types": "6.18.0", - "@sentry/utils": "6.18.0", + "@sentry/types": "7.9.0", + "@sentry/utils": "7.9.0", "tslib": "^1.9.3" }, "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/minimal": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.18.0.tgz", - "integrity": "sha512-QkkWOhX3NMycUNLj96thMQ0BclmfxE2VdDf9ZqRkvdFzxI1FVY5NEArqD4wtlrCIoYN1ioAYrvdb48/BTuGung==", - "dependencies": { - "@sentry/hub": "6.18.0", - "@sentry/types": "6.18.0", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/@sentry/types": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.18.0.tgz", - "integrity": "sha512-SypDwXL1URE/XLkP4Ve+pFs41e+2OUYZ0lCimNreQQv46//pFXxP3LwU9Tc0Az4ZfxXnGiwofvt73XyBq9VpRQ==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.9.0.tgz", + "integrity": "sha512-VGnUgELVMpGJCYW1triO+5XSyaPjB2Zu6esUgbb8iJ5bi+OWtxklixXgwhdaTb0FDzmRL/T/pckmrIuBTLySHQ==", "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/@sentry/utils": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.18.0.tgz", - "integrity": "sha512-mKegOabkAjoUHfokjI5oi3CMez5GD3xXOrBFcLVc9GFDXCgNMdYnHyEn/mmy8PikFdGHxZ3oI/16ZGU22wi5aw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.9.0.tgz", + "integrity": "sha512-4f9TZvAVopgG7Lp1TcPSekSX1Ashk68Et4T8Y+60EVX5se19i0hpytbHWWwrXSrb3w0KpGANk0byoZkdaTgkYA==", "dependencies": { - "@sentry/types": "6.18.0", + "@sentry/types": "7.9.0", "tslib": "^1.9.3" }, "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/@sentry/vue": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@sentry/vue/-/vue-6.18.0.tgz", - "integrity": "sha512-UbShhHPXvCvBMssFxxl+rzaIwH9jliByJzkYnW3MmJMP9YhB++M42FrvTaLZa0wlsYGR71j6+lG3FNrdyjFDGg==", - "dependencies": { - "@sentry/browser": "6.18.0", - "@sentry/core": "6.18.0", - "@sentry/minimal": "6.18.0", - "@sentry/types": "6.18.0", - "@sentry/utils": "6.18.0", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@sentry/vue/-/vue-7.9.0.tgz", + "integrity": "sha512-oHTNc31xfOqxASEQXzZ9LxN2cbKlhQNuaLQZEG4W/3mEWwJM/TLwd/0pWQPUmUQDuQlxTb56hEqCbaCPNTOPqw==", + "dependencies": { + "@sentry/browser": "7.9.0", + "@sentry/core": "7.9.0", + "@sentry/types": "7.9.0", + "@sentry/utils": "7.9.0", "tslib": "^1.9.3" }, "engines": { - "node": ">=6" + "node": ">=8" }, "peerDependencies": { - "vue": "2.x || 3.x", - "vue-router": "3.x || 4.x" + "vue": "2.x || 3.x" } }, "node_modules/@sideway/address": { @@ -21130,72 +21114,60 @@ } }, "@sentry/browser": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.18.0.tgz", - "integrity": "sha512-fQZdFs0jnBijVmhDWNIsHD0rGsIXup/1UYgMZqdBYYSBBQ7Ffpw+6nX1/vyKsCpLdgYhbi88eK/XO+QHtUJE2w==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.9.0.tgz", + "integrity": "sha512-R0/EatdSBPZ+orsD5Mu/Gq8XmEfr/KCzJv05S35GVPDkIgczIJ2AYlHgchnEO0m63jDFyWLzUteQmPZ3pao9PQ==", "requires": { - "@sentry/core": "6.18.0", - "@sentry/types": "6.18.0", - "@sentry/utils": "6.18.0", + "@sentry/core": "7.9.0", + "@sentry/types": "7.9.0", + "@sentry/utils": "7.9.0", "tslib": "^1.9.3" } }, "@sentry/core": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.18.0.tgz", - "integrity": "sha512-I3iQVfMWHXR/LtevJg83aD7UAiUBLz1xAW8y3gd5lJej96UNv/4TbCmKZumYnEJMXf8EcFlg8t48W0Bl1GxhEg==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.9.0.tgz", + "integrity": "sha512-WVGd2hV7Clcpl7/GL8LsRr4Zk9o/7o4rZHfs1Qed5lMRNYcxiMwucC1CYILVpJqVfY+8vIRP9v9Ss9ta2VUikw==", "requires": { - "@sentry/hub": "6.18.0", - "@sentry/minimal": "6.18.0", - "@sentry/types": "6.18.0", - "@sentry/utils": "6.18.0", + "@sentry/hub": "7.9.0", + "@sentry/types": "7.9.0", + "@sentry/utils": "7.9.0", "tslib": "^1.9.3" } }, "@sentry/hub": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.18.0.tgz", - "integrity": "sha512-E2GrrNcidyT67ONU3btHO5vyS1bPQNdWqC09sUc1F3q/nQyvc7L2W09TKY2veaMZQtC9EU760fTG1hMmgGwPmw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-7.9.0.tgz", + "integrity": "sha512-KzPbGCB5mONgsXEzqHy6uOaOuqLnhmFmSpGg+M03J6UJnJaNM7nrNp80MhStmjLMq6whEYVE07DrMAn3+iaQdg==", "requires": { - "@sentry/types": "6.18.0", - "@sentry/utils": "6.18.0", - "tslib": "^1.9.3" - } - }, - "@sentry/minimal": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.18.0.tgz", - "integrity": "sha512-QkkWOhX3NMycUNLj96thMQ0BclmfxE2VdDf9ZqRkvdFzxI1FVY5NEArqD4wtlrCIoYN1ioAYrvdb48/BTuGung==", - "requires": { - "@sentry/hub": "6.18.0", - "@sentry/types": "6.18.0", + "@sentry/types": "7.9.0", + "@sentry/utils": "7.9.0", "tslib": "^1.9.3" } }, "@sentry/types": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.18.0.tgz", - "integrity": "sha512-SypDwXL1URE/XLkP4Ve+pFs41e+2OUYZ0lCimNreQQv46//pFXxP3LwU9Tc0Az4ZfxXnGiwofvt73XyBq9VpRQ==" + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.9.0.tgz", + "integrity": "sha512-VGnUgELVMpGJCYW1triO+5XSyaPjB2Zu6esUgbb8iJ5bi+OWtxklixXgwhdaTb0FDzmRL/T/pckmrIuBTLySHQ==" }, "@sentry/utils": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.18.0.tgz", - "integrity": "sha512-mKegOabkAjoUHfokjI5oi3CMez5GD3xXOrBFcLVc9GFDXCgNMdYnHyEn/mmy8PikFdGHxZ3oI/16ZGU22wi5aw==", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.9.0.tgz", + "integrity": "sha512-4f9TZvAVopgG7Lp1TcPSekSX1Ashk68Et4T8Y+60EVX5se19i0hpytbHWWwrXSrb3w0KpGANk0byoZkdaTgkYA==", "requires": { - "@sentry/types": "6.18.0", + "@sentry/types": "7.9.0", "tslib": "^1.9.3" } }, "@sentry/vue": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@sentry/vue/-/vue-6.18.0.tgz", - "integrity": "sha512-UbShhHPXvCvBMssFxxl+rzaIwH9jliByJzkYnW3MmJMP9YhB++M42FrvTaLZa0wlsYGR71j6+lG3FNrdyjFDGg==", - "requires": { - "@sentry/browser": "6.18.0", - "@sentry/core": "6.18.0", - "@sentry/minimal": "6.18.0", - "@sentry/types": "6.18.0", - "@sentry/utils": "6.18.0", + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@sentry/vue/-/vue-7.9.0.tgz", + "integrity": "sha512-oHTNc31xfOqxASEQXzZ9LxN2cbKlhQNuaLQZEG4W/3mEWwJM/TLwd/0pWQPUmUQDuQlxTb56hEqCbaCPNTOPqw==", + "requires": { + "@sentry/browser": "7.9.0", + "@sentry/core": "7.9.0", + "@sentry/types": "7.9.0", + "@sentry/utils": "7.9.0", "tslib": "^1.9.3" } }, diff --git a/web_client/package.json b/web_client/package.json index 0024cfa1..f8dc50f0 100644 --- a/web_client/package.json +++ b/web_client/package.json @@ -10,7 +10,7 @@ "dependencies": { "@girder/oauth-client": "^0.7.7", "@mdi/font": "^6.5.95", - "@sentry/vue": "^6.17.6", + "@sentry/vue": "^7.9.0", "@types/idle-js": "^1.2.1", "@types/jest": "^27.0.1", "@typescript-eslint/eslint-plugin": "^4.33.0", diff --git a/web_client/src/components/WindowWidget.vue b/web_client/src/components/WindowWidget.vue index 0d73e668..491245be 100644 --- a/web_client/src/components/WindowWidget.vue +++ b/web_client/src/components/WindowWidget.vue @@ -52,6 +52,7 @@ export default defineComponent({ } function updateFromRange([v0, v1]) { if (windowLocked.value) return; + if (v0 === currentRange.value[0] && v1 === currentRange.value[1]) return; const ww = v1 - v0; const wl = v0 + Math.ceil(ww / 2); updateRender(ww, wl); @@ -88,7 +89,7 @@ export default defineComponent({ window.addEventListener('click', (event: Event) => { const protectedDiv = document.getElementById('windowLockWidget'); const target = event.target as HTMLElement; - if (!protectedDiv.contains(target)) { + if (!protectedDiv || !protectedDiv.contains(target)) { showLockOptions.value = false; } }); @@ -177,7 +178,7 @@ export default defineComponent({ single-line type="number" style="width: 60px" - @change="$set(currentRange, 0, $event)" + @input="(value) => currentRange = [value, currentRange[1]]" /> diff --git a/web_client/src/django.ts b/web_client/src/django.ts index 2db4aeed..19691f45 100644 --- a/web_client/src/django.ts +++ b/web_client/src/django.ts @@ -52,7 +52,11 @@ const djangoClient = { try { await apiClient.post('/logout/', undefined, { withCredentials: true }); } finally { - await oauthClient.logout(); + await oauthClient.logout().then( + async () => { + await oauthClient.redirectToLogin(); + }, + ); } }, async MIQAConfig() { @@ -202,11 +206,6 @@ apiClient.interceptors.response.use(null, (error) => { if (error?.response?.status === 401) { djangoClient.logout(); } - let msg = error?.response?.data?.detail || 'No response from server'; - if (error?.response?.status === 403) { - msg = 'You are not allowed to perform this action.'; - } - throw new Error(msg); }); export { apiClient, oauthClient }; diff --git a/web_client/src/store/index.ts b/web_client/src/store/index.ts index accbf44a..2abb8456 100644 --- a/web_client/src/store/index.ts +++ b/web_client/src/store/index.ts @@ -13,7 +13,7 @@ import WorkerPool from 'itk/WorkerPool'; import ITKHelper from 'vtk.js/Sources/Common/DataModel/ITKHelper'; import djangoRest, { apiClient } from '@/django'; import { - Project, ProjectTaskOverview, User, Frame, ProjectSettings, Scan, + Project, ProjectTaskOverview, User, ProjectSettings, Scan, } from '@/types'; import axios from 'axios'; import ReaderFactory from '../utils/ReaderFactory'; @@ -326,17 +326,8 @@ const { const currentFrame = state.currentFrameId ? state.frames[state.currentFrameId] : null; const scan = state.scans[currentFrame.scan]; if (!scan) { - const nextFrame = Object.entries(state.frames).map( - ([, frameInfo]) => frameInfo, - ).filter( - (frameInfo: Frame) => frameInfo.scan === Object.keys(state.scans)[0], - )[0][0]; - // Scan removed from list by review mode, return sparse object - // and let the component navigate away - // since navigation should not happen in the store - return { - downTo: nextFrame, - }; + // scan was removed from list by review mode; do nothing + return {}; } const experiment = currentFrame.experiment ? state.experiments[currentFrame.experiment] : null; @@ -463,6 +454,7 @@ const { state.globalSettings = settings; }, setTaskOverview(state, taskOverview: ProjectTaskOverview) { + if (!taskOverview) return; if (taskOverview.scan_states) { state.projects.find( (project) => project.id === taskOverview.project_id, @@ -473,7 +465,7 @@ const { ).length, }; } - if (taskOverview.project_id === state.currentProject.id) { + if (state.currentProject && taskOverview.project_id === state.currentProject.id) { state.currentTaskOverview = taskOverview; Object.values(store.state.allScans).forEach((scan: Scan) => { if (taskOverview.scan_states[scan.id] && taskOverview.scan_states[scan.id] !== 'unreviewed') { @@ -843,9 +835,7 @@ const { // check for window lock expiry if (state.windowLocked.lock) { - console.log(state.windowLocked); const { currentViewData } = getters; - console.log(currentViewData); const unlock = () => { commit('setWindowLocked', { lock: false,