From a5ea86c0b92e04ad355f7599e55be90f71228d4c Mon Sep 17 00:00:00 2001 From: eheinrich Date: Tue, 18 Aug 2020 13:18:06 -0700 Subject: [PATCH 01/17] Archive authentication, start on request detail page --- package-lock.json | 45 +++++ package.json | 3 + public/config/urls.json | 4 +- src/archive.js | 31 ++++ src/components/RequestRow.vue | 240 ++++++++++++++++++++++++++ src/components/RequestgroupHeader.vue | 148 ++++++++++++++++ src/components/RequestgroupsList.vue | 15 +- src/main.js | 23 ++- src/router/index.js | 13 +- src/store/index.js | 30 ++++ src/utils.js | 13 +- src/views/Request.vue | 87 ++++++++++ vue.config.js | 13 ++ 13 files changed, 645 insertions(+), 20 deletions(-) create mode 100644 src/archive.js create mode 100644 src/components/RequestRow.vue create mode 100644 src/components/RequestgroupHeader.vue create mode 100644 src/views/Request.vue diff --git a/package-lock.json b/package-lock.json index b9ab600..e6b9d58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2735,6 +2735,11 @@ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.5.0.tgz", "integrity": "sha512-Z93QoXvodoVslA+PWNdk23Hze4RBYIkpb5h8I2HY2Tu2h7A0LpAgLcyrhrSUyo2/Oxm2l1fRZPs1e5hnxnliXA==" }, + "bootstrap-table": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/bootstrap-table/-/bootstrap-table-1.17.1.tgz", + "integrity": "sha512-FEBd8xwaOLOLwtdme3HVr0RI0TPnvoMd/M3iVSX1w5ZLrbbMHU+Eh0YbGvOLSAfduL+9rDZ/cG/5Qe057o0cIg==" + }, "bootstrap-vue": { "version": "2.15.0", "resolved": "https://registry.npmjs.org/bootstrap-vue/-/bootstrap-vue-2.15.0.tgz", @@ -5196,6 +5201,11 @@ } } }, + "emitter-component": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.1.tgz", + "integrity": "sha1-Bl4tvtaVm/RwZ57avq95gdEAOrY=" + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -6459,6 +6469,11 @@ "pify": "^4.0.1" } }, + "hammerjs": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", + "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=" + }, "handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -7607,6 +7622,11 @@ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz", "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==" }, + "jquery-file-download": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/jquery-file-download/-/jquery-file-download-1.4.6.tgz", + "integrity": "sha1-/ly0XUcE+Jflt2I+zoc3EaqLRuo=" + }, "js-beautify": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.11.0.tgz", @@ -7796,6 +7816,11 @@ "verror": "1.10.0" } }, + "keycharm": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.2.0.tgz", + "integrity": "sha1-+m6i5DuQpoAohD0n8gddNajD5vk=" + }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -10601,6 +10626,14 @@ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, + "propagating-hammerjs": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/propagating-hammerjs/-/propagating-hammerjs-1.4.7.tgz", + "integrity": "sha512-oW9Wd+W2Tp5uOz6Fh4mEU7p+FoyU85smLH/mPga83Loh0pHa6AH4ZHGywvwMk3TWP31l7iUsvJyW265p4Ipwrg==", + "requires": { + "hammerjs": "^2.0.8" + } + }, "proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -12833,6 +12866,18 @@ "extsprintf": "^1.2.0" } }, + "vis": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/vis/-/vis-4.19.1.tgz", + "integrity": "sha1-XO9OcuHwNypOWrUCUjuIaHseICc=", + "requires": { + "emitter-component": "^1.1.1", + "hammerjs": "^2.0.8", + "keycharm": "^0.2.0", + "moment": "^2.17.1", + "propagating-hammerjs": "^1.4.6" + } + }, "vm-browserify": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", diff --git a/package.json b/package.json index e0d019f..9b50f97 100644 --- a/package.json +++ b/package.json @@ -11,14 +11,17 @@ }, "dependencies": { "bootstrap": "^4.3.1", + "bootstrap-table": "^1.14.2", "bootstrap-vue": "^2.15.0", "core-js": "^3.6.5", "chart.js": "^2.8.0", "chartjs-plugin-annotation": "^0.5.7", "jquery": "^3.5.0", + "jquery-file-download": "^1.4.6", "lodash": "^4.17.13", "moment": "^2.22.1", "popper.js": "^1.16.1", + "vis": "4.19.1", "vue": "^2.6.11", "vue-router": "^3.2.0", "vuex": "^3.4.0" diff --git a/public/config/urls.json b/public/config/urls.json index 6f2c6f7..7fb0fa9 100644 --- a/public/config/urls.json +++ b/public/config/urls.json @@ -1,5 +1,7 @@ { "observationPortalApiUrl": "http://127.0.0.1:8000", "archiveApiUrl": "http://127.0.0.1:8000", - "simbadServiceUrl": "http://127.0.0.1:8000" + "archiveClientUrl": "http://127.0.0.1:8000", + "simbadServiceUrl": "http://127.0.0.1:8000", + "thumbnailServiceUrl": "http://127.0.0.1:8000" } diff --git a/src/archive.js b/src/archive.js new file mode 100644 index 0000000..2b55595 --- /dev/null +++ b/src/archive.js @@ -0,0 +1,31 @@ +import $ from 'jquery'; +import 'jquery-file-download'; + +export { downloadZip, downloadAll }; + + +function downloadZip(frameIds, archiveRoot, archiveToken) { + let postData = {}; + for (let i = 0; i < frameIds.length; i++) { + postData['frame_ids[' + i + ']'] = frameIds[i]; + } + postData['auth_token'] = archiveToken; + $.fileDownload(archiveRoot + 'frames/zip/', { + httpMethod: 'POST', + data: postData + }); +} + +function downloadAll(requestId, archiveRoot, archiveClientUrl, archiveToken) { + $.getJSON(archiveRoot + 'frames/?limit=1000&REQNUM=' + requestId, function (data) { + if (data.count > 1000) { + alert('Over 1000 products found. Please use ' + archiveClientUrl + ' to download your data'); + return false; + } + let frameIds = []; + for (let i = 0; i < data.results.length; i++) { + frameIds.push(data.results[i].id); + } + downloadZip(frameIds, archiveRoot, archiveToken); + }); +} diff --git a/src/components/RequestRow.vue b/src/components/RequestRow.vue new file mode 100644 index 0000000..23e5b0d --- /dev/null +++ b/src/components/RequestRow.vue @@ -0,0 +1,240 @@ + + diff --git a/src/components/RequestgroupHeader.vue b/src/components/RequestgroupHeader.vue new file mode 100644 index 0000000..02242c0 --- /dev/null +++ b/src/components/RequestgroupHeader.vue @@ -0,0 +1,148 @@ + + diff --git a/src/components/RequestgroupsList.vue b/src/components/RequestgroupsList.vue index f3f6ac9..56d2f61 100644 --- a/src/components/RequestgroupsList.vue +++ b/src/components/RequestgroupsList.vue @@ -220,7 +220,7 @@
-
{{ data.item.state }}
+
{{ data.item.state }}
{{ data.item.modified | formatDate }}
@@ -272,7 +272,7 @@ import $ from 'jquery'; import _ from 'lodash'; -import { timeFromNow, formatDate, copyObject, stateToBsClass } from '@/utils.js'; +import { timeFromNow, formatDate, copyObject, stateToBsClass, stateToIcon } from '@/utils.js'; export default { name: 'RequestgroupsList', @@ -289,15 +289,8 @@ export default { stateToBsClass: function(state, prefix) { return stateToBsClass(state, prefix); }, - startToIcon: function(state) { - let stateMap = { - 'PENDING': 'sync', - 'SCHEDULED': 'sync', - 'COMPLETED': 'check', - 'WINDOW_EXPIRED': 'times', - 'CANCELED': 'times', - } - return 'fa-' + stateMap[state] + stateToIcon: function(state) { + return stateToIcon(state); }, valueOrDefault: function(value, defaultValue) { return value || defaultValue; diff --git a/src/main.js b/src/main.js index 2b40aa8..a7ee4b0 100644 --- a/src/main.js +++ b/src/main.js @@ -11,11 +11,6 @@ import $ from 'jquery'; import { addCsrfProtection } from '@/utils.js'; $(document).ajaxSend(addCsrfProtection); -$.ajaxSetup({ - xhrFields: { - withCredentials: true - } -}); Vue.use(BootstrapVue); @@ -30,7 +25,25 @@ getRuntimeConfig().then(function(json) { store.commit('setRuntimeConfig', { observationPortalApi: process.env.VUE_APP_OBSERVATION_PORTAL_API_URL || json.observationPortalApiUrl, archiveApi: process.env.VUE_APP_ARCHIVE_API_URL || json.archiveApiUrl, + archiveUi: process.env.VUE_APP_ARCHIVE_UI_URL || json.archiveUiUrl, simbadService: process.env.VUE_APP_SIMBAD_SERVICE_URL || json.simbadServiceUrl, + thumbnailService: process.env.VUE_APP_THUMBNAILS_SERVICE_URL || json.thumbnailServiceUrl, + }); + + // Include credentials when sending requests to the observation portal. + $(document).ajaxSend(function(event, xhr, settings) { + if (settings.url.startsWith(store.state.urls.observationPortalApi)) { + settings.xhrFields = { + withCredentials: true + }; + } + }); + + // Add the archive token to a request being sent to the archive api or the thumbservice + $.ajaxPrefilter(function (options, originalOptions, jqXHR) { + if ((options.url.indexOf(store.state.urls.archiveApi) >= 0 || options.url.indexOf(store.state.urls.thumbnailService) >= 0) && store.state.archiveAuthToken) { + jqXHR.setRequestHeader('Authorization', 'Token ' + store.state.archiveAuthToken); + } }); store.dispatch('getProfileData').then(() => { diff --git a/src/router/index.js b/src/router/index.js index 8f0f04b..dac9ae7 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -10,6 +10,7 @@ import AcceptTerms from '../views/AcceptTerms.vue'; import AccountsGet from '../views/AccountsGet.vue'; import AccountsForm from '../views/AccountsForm.vue'; import AccountRemovalRequest from '../views/AccountRemovalRequest.vue'; +import Request from '../views/Request.vue'; import store from '../store/index.js'; import _ from 'lodash'; @@ -43,16 +44,24 @@ const routes = [ }, { path: '/proposals/:id', - name: 'proposalsDetail', + name: 'proposalDetail', // TODO: Update component component: NotFound }, { path: '/requestgroups/:id', - name: 'requestgroupsDetail', + name: 'requestgroupDetail', // TODO: Update component component: NotFound }, + { + path: '/requests/:id', + name: 'requestDetail', + component: Request, + meta: { + title: 'Request' + } + }, { path: '/apply', name: 'apply', diff --git a/src/store/index.js b/src/store/index.js index 97d157d..04f875d 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -18,6 +18,7 @@ export default new Vuex.Store({ userIsAuthenticated: false, userAcceptedTerms: false, messages: [], + archiveToken: '', urls: {} }, mutations: { @@ -40,6 +41,9 @@ export default new Vuex.Store({ setRuntimeConfig (state, payload) { state.urls = payload; }, + setArchiveToken(state, token) { + state.archiveToken = token; + }, addMessage (state, newMessage) { let messageAlreadyInList = false; for (let message of state.messages) { @@ -81,6 +85,32 @@ export default new Vuex.Store({ } }); }) + }, + getArchiveToken(context) { + return new Promise((resolve, reject) => { + if (context.state.archiveToken === '') { + // TODO: Should I pull new profile info if the bearer token isnt present? + $.ajax({ + method: 'POST', + dataType: 'json', + url: context.state.urls.archiveApi + '/api-token-auth/', + headers: { + 'Authorization': 'Bearer ' + context.state.profile.tokens.archive + }, + success: function(response) { + context.commit('setArchiveToken', response.token); + resolve(); + }, + error: function(response) { + console.log('failed to get token', response) + reject(); + } + }) + } else { + // The archive token is already in the store, no need to retrieve it again + resolve(); + } + }) } }, modules: { diff --git a/src/utils.js b/src/utils.js index 5442d78..569d9d8 100644 --- a/src/utils.js +++ b/src/utils.js @@ -316,6 +316,17 @@ function stateToBsClass(state, classPrefix) { return classPrefix + '-' + state_map[state] } +function stateToIcon(state) { + let stateMap = { + 'PENDING': 'sync', + 'SCHEDULED': 'sync', + 'COMPLETED': 'check', + 'WINDOW_EXPIRED': 'times', + 'CANCELED': 'times', + } + return 'fa fa-fw fa-' + stateMap[state] +} + let siteToColor = { 'tfn': '#263c6f', // dark blue 'elp': '#700000', // dark red @@ -385,5 +396,5 @@ export { formatDate, copyObject, formatField, datetimeFormat, timeFromNow, collapseMixin, siteToColor, siteCodeToName, arcDefaultExposureTime, lampFlatDefaultExposureTime, observatoryCodeToNumber, telescopeCodeToName, colorPalette, julianToModifiedJulian, getFieldDescription, decimalRaToSexigesimal, decimalDecToSexigesimal, tooltipConfig, addCsrfProtection, - extractTopLevelErrors, stateToBsClass + extractTopLevelErrors, stateToBsClass, stateToIcon }; diff --git a/src/views/Request.vue b/src/views/Request.vue new file mode 100644 index 0000000..a6d768e --- /dev/null +++ b/src/views/Request.vue @@ -0,0 +1,87 @@ + + diff --git a/vue.config.js b/vue.config.js index 882a6f7..2bc69d1 100644 --- a/vue.config.js +++ b/vue.config.js @@ -1,3 +1,5 @@ +const path = require('path'); + module.exports = { chainWebpack: config => { config @@ -6,5 +8,16 @@ module.exports = { args[0].title = 'LCO Observation Portal' return args }) + }, + configureWebpack: config => { + return { + resolve: { + alias: { + // This is needed for jquery-file-download/src/Scripts/jquery.fileDownload.js to work + 'jquery': path.join(__dirname, 'node_modules/jquery/src/jquery'), + } + } + }; } + } From ae7abae8d77bb7716b871626346001a5d7fcf09d Mon Sep 17 00:00:00 2001 From: eheinrich Date: Tue, 18 Aug 2020 18:11:35 -0700 Subject: [PATCH 02/17] Add requestgroup detail page --- src/components/RequestRow.vue | 59 ++++++------ src/components/RequestgroupHeader.vue | 2 +- src/components/RequestgroupsList.vue | 4 +- src/router/index.js | 9 +- src/store/index.js | 5 +- src/views/Request.vue | 38 +++++--- src/views/Requestgroup.vue | 130 ++++++++++++++++++++++++++ 7 files changed, 193 insertions(+), 54 deletions(-) create mode 100644 src/views/Requestgroup.vue diff --git a/src/components/RequestRow.vue b/src/components/RequestRow.vue index 23e5b0d..e1fe69b 100644 --- a/src/components/RequestRow.vue +++ b/src/components/RequestRow.vue @@ -1,11 +1,11 @@ diff --git a/src/views/Requestgroup.vue b/src/views/Requestgroup.vue new file mode 100644 index 0000000..b28b48c --- /dev/null +++ b/src/views/Requestgroup.vue @@ -0,0 +1,130 @@ + + From 78b50e93e6ecf6ecb20798cd7d61bce0c1ae0621 Mon Sep 17 00:00:00 2001 From: eheinrich Date: Wed, 19 Aug 2020 09:20:29 -0700 Subject: [PATCH 03/17] Add request detail --- src/archive.js | 13 +- src/assets/css/plot_style.css | 154 +++++++ src/components/Airmass.vue | 190 +++++++++ src/components/AirmassTelescopeStates.vue | 65 +++ src/components/ArchiveTable.vue | 152 +++++++ src/components/ObservationHistory.vue | 239 +++++++++++ src/components/RequestDetail.vue | 475 ++++++++++++++++++++++ src/components/RequestRow.vue | 109 +++-- src/components/TelescopeStates.vue | 168 ++++++++ src/components/Thumbnail.vue | 84 ++++ src/components/util/PlotControls.vue | 40 ++ src/components/util/plotMixins.js | 19 + src/views/Request.vue | 71 ++-- src/views/Requestgroup.vue | 76 ++-- 14 files changed, 1720 insertions(+), 135 deletions(-) create mode 100644 src/assets/css/plot_style.css create mode 100644 src/components/Airmass.vue create mode 100644 src/components/AirmassTelescopeStates.vue create mode 100644 src/components/ArchiveTable.vue create mode 100644 src/components/ObservationHistory.vue create mode 100644 src/components/RequestDetail.vue create mode 100644 src/components/TelescopeStates.vue create mode 100644 src/components/Thumbnail.vue create mode 100644 src/components/util/PlotControls.vue create mode 100644 src/components/util/plotMixins.js diff --git a/src/archive.js b/src/archive.js index 2b55595..7b7fce9 100644 --- a/src/archive.js +++ b/src/archive.js @@ -1,8 +1,7 @@ import $ from 'jquery'; import 'jquery-file-download'; -export { downloadZip, downloadAll }; - +export { downloadZip, downloadAll, getLatestFrame }; function downloadZip(frameIds, archiveRoot, archiveToken) { let postData = {}; @@ -12,12 +11,12 @@ function downloadZip(frameIds, archiveRoot, archiveToken) { postData['auth_token'] = archiveToken; $.fileDownload(archiveRoot + 'frames/zip/', { httpMethod: 'POST', - data: postData + data: postData, }); } function downloadAll(requestId, archiveRoot, archiveClientUrl, archiveToken) { - $.getJSON(archiveRoot + 'frames/?limit=1000&REQNUM=' + requestId, function (data) { + $.getJSON(archiveRoot + 'frames/?limit=1000&REQNUM=' + requestId, function(data) { if (data.count > 1000) { alert('Over 1000 products found. Please use ' + archiveClientUrl + ' to download your data'); return false; @@ -29,3 +28,9 @@ function downloadAll(requestId, archiveRoot, archiveClientUrl, archiveToken) { downloadZip(frameIds, archiveRoot, archiveToken); }); } + +function getLatestFrame(requestId, archiveRoot, callback) { + $.getJSON(archiveRoot + 'frames/?ordering=-id&limit=1&REQNUM=' + requestId, function(data) { + callback(data.results[0]); + }); +} diff --git a/src/assets/css/plot_style.css b/src/assets/css/plot_style.css new file mode 100644 index 0000000..f3c8256 --- /dev/null +++ b/src/assets/css/plot_style.css @@ -0,0 +1,154 @@ +.vis-line-graph text{ + display: none; + pointer-events: none; +} + +.vis-point { + pointer-events: all; +} + +.vis-item.vis-range{ + border-radius: 0px; +} + +div.vis-tooltip{ + word-wrap: break-word; + max-width: 80em; + white-space: normal; +} + +.AVAILABLE { + background-color: deepskyblue; + border-color: deepskyblue; +} + +.OFFLINE { + background-color: orange; + border-color: orange; +} + +.NOT_OK_TO_OPEN { + background-color: purple; + border-color: purple; +} + +.SITE_AGENT_UNRESPONSIVE { + background-color: black; + border-color: black; +} + +.SEQUENCER_DISABLED { + background-color: orange; + border-color: orange; +} + +.ENCLOSURE_INTERLOCK { + background-color: purple; + border-color: purple; +} + +.SEQUENCER_UNAVAILABLE { + background-color: purple; + border-color: purple; +} + +.SCHEDULED { + background-color: #c8dac2; + border-color: #658e57; +} + +.NOT_ATTEMPTED { + background-color: olive; + border-color: darkolivegreen; +} + +.CANCELED { + background-color: grey; + border-color: darkgrey; +} + +.IN_PROGRESS { + background-color: lightgreen; + border-color: green; +} + +.COMPLETED { + color: white; + background-color: green; + border-color: darkgreen; +} + +.PARTIALLY-COMPLETED { + background-color: yellow; + border-color: goldenrod; +} + +.FAILED { + background-color: red; + border-color: maroon; +} + +.ABORTED { + background-color: orange; + border-color: darkorange; +} + +.NO_CONNECTION { + background-color: black; + border-color: darkgrey; +} + + +.legend-item { + width: 18px; + height: 12px; + display: inline-block; +} + +.panel-default>.panel-heading:hover{ + background-color: #dcdcdc; + border-color: #cfcfcf; +} + +.panel-success>.panel-heading:hover{ + background-color: #c1e2b3; + border-color: #b2dba1; +} + +.panel-warning>.panel-heading:hover{ + background-color: #f7ecb5; + border-color: #f5e79e; +} + +.plot_controls { + position: absolute; + right: 0; + margin-right: 22px; + margin-top: 4px; + z-index: 9999; +} + +.clickable-thumbnail { + display:block; + width: 100%; + height: 100%; +} + +.clickable-thumbnail:visited, .clickable-thumbnail:link{ + text-decoration: none; + color: lightgrey; +} + +.clickable-thumbnail:hover{ + color:white; +} + +.width180 { + width: 180px; + font-size: 21px; +} + +.width120 { + width: 120px; + font-size: 21px; +} diff --git a/src/components/Airmass.vue b/src/components/Airmass.vue new file mode 100644 index 0000000..a6ab9ec --- /dev/null +++ b/src/components/Airmass.vue @@ -0,0 +1,190 @@ + + diff --git a/src/components/AirmassTelescopeStates.vue b/src/components/AirmassTelescopeStates.vue new file mode 100644 index 0000000..f2e011e --- /dev/null +++ b/src/components/AirmassTelescopeStates.vue @@ -0,0 +1,65 @@ + + diff --git a/src/components/ArchiveTable.vue b/src/components/ArchiveTable.vue new file mode 100644 index 0000000..cd0aeb5 --- /dev/null +++ b/src/components/ArchiveTable.vue @@ -0,0 +1,152 @@ + + + diff --git a/src/components/ObservationHistory.vue b/src/components/ObservationHistory.vue new file mode 100644 index 0000000..057c8b0 --- /dev/null +++ b/src/components/ObservationHistory.vue @@ -0,0 +1,239 @@ + + diff --git a/src/components/RequestDetail.vue b/src/components/RequestDetail.vue new file mode 100644 index 0000000..9eeacbc --- /dev/null +++ b/src/components/RequestDetail.vue @@ -0,0 +1,475 @@ + + + diff --git a/src/components/RequestRow.vue b/src/components/RequestRow.vue index e1fe69b..c252ee2 100644 --- a/src/components/RequestRow.vue +++ b/src/components/RequestRow.vue @@ -1,26 +1,15 @@ -
{{ schedulingInformation.error }}
+
+ {{ schedulingInformation.error }} +
@@ -100,20 +87,20 @@ import { stateToBsClass, stateToIcon, formatDate } from '@/utils.js'; import { downloadAll } from '@/archive.js'; export default { - name: "RequestRow", + name: 'RequestRow', props: { request: { type: Object, }, instruments: { - type: Object + type: Object, }, link: { type: Boolean, - default: false - } + default: false, + }, }, - data: function() { + data: function () { return { thumbnailUrl: '', thumbnailError: '', @@ -121,9 +108,9 @@ export default { frame: {}, schedulingInformation: { found: false, - error: '' - } - } + error: '', + }, + }; }, filters: { stateToBsClass: function (state, prefix) { @@ -134,31 +121,31 @@ export default { }, formatDate(value) { return formatDate(value); - } + }, }, computed: { userIsAuthenticated: function () { return this.$store.state.userIsAuthenticated; }, - observationPortalApiUrl: function() { + observationPortalApiUrl: function () { return this.$store.state.urls.observationPortalApi; }, - archiveApiUrl: function() { + archiveApiUrl: function () { return this.$store.state.urls.archiveApi; }, - thumbnailServiceUrl: function() { + thumbnailServiceUrl: function () { return this.$store.state.urls.thumbnailService; }, - archiveClientUrl: function() { + archiveClientUrl: function () { return this.$store.state.urls.archiveClient; }, - archiveToken: function() { + archiveToken: function () { return this.$store.state.archiveToken; }, - requestApiUrl: function() { + requestApiUrl: function () { return this.$store.state.urls.observationPortalApi + '/api/requests/' + this.request.id + '/'; }, - instrumentName: function() { + instrumentName: function () { let instrumentType = _.get(this.request, ['configurations', 0, 'instrument_type']); if (instrumentType && instrumentType in this.instruments) { return this.instruments[instrumentType].name; @@ -166,11 +153,11 @@ export default { return instrumentType; } }, - archiveDataIsAvailable: function() { + archiveDataIsAvailable: function () { return this.frame.id ? true : false; - } + }, }, - created: function() { + created: function () { let that = this; this.$store.dispatch('getArchiveToken').then(() => { that.loadThumbnail(); @@ -180,17 +167,17 @@ export default { } }, methods: { - downloadAllData: function() { + downloadAllData: function () { downloadAll(this.request.id, this.archiveApiUrl, this.archiveClientUrl, this.archiveToken); }, - loadThumbnail: function() { + loadThumbnail: function () { const thumbnailSize = 75; let that = this; $.ajax({ method: 'GET', dataType: 'json', url: this.archiveApiUrl + '/frames/?ordering=-id&limit=1&REQNUM=' + this.request.id, - success: function(response) { + success: function (response) { if (response.results.length === 0) { that.archiveError = 'Waiting on data to become available'; } else { @@ -198,36 +185,36 @@ export default { $.ajax({ url: that.thumbnailServiceUrl + '/' + that.frame.id + '/?height=' + thumbnailSize, dataType: 'json', - success: function(response) { - that.thumbnailUrl = response.url + success: function (response) { + that.thumbnailUrl = response.url; }, - error: function() { + error: function () { that.thumbnailError = 'Could not load thumbnail for this file'; - } - }) + }, + }); } - } - }) + }, + }); }, - getPendingDetails: function() { + getPendingDetails: function () { let that = this; - $.getJSON(this.observationPortalApiUrl + '/api/requests/' + this.request.id + '/observations/?exclude_canceled=true', function(data) { + $.getJSON(this.observationPortalApiUrl + '/api/requests/' + this.request.id + '/observations/?exclude_canceled=true', function (data) { if (data.length > 0) { data = data.reverse(); // get the latest non canceled block that.schedulingInformation = { found: true, site: data[0].site, start: data[0].start, - end: data[0].end - } + end: data[0].end, + }; } else { that.schedulingInformation = { found: false, - error: 'No scheduling information found' - } + error: 'No scheduling information found', + }; } }); - } - } + }, + }, }; diff --git a/src/components/TelescopeStates.vue b/src/components/TelescopeStates.vue new file mode 100644 index 0000000..fcf6af7 --- /dev/null +++ b/src/components/TelescopeStates.vue @@ -0,0 +1,168 @@ + + diff --git a/src/components/Thumbnail.vue b/src/components/Thumbnail.vue new file mode 100644 index 0000000..b876fc2 --- /dev/null +++ b/src/components/Thumbnail.vue @@ -0,0 +1,84 @@ + + + diff --git a/src/components/util/PlotControls.vue b/src/components/util/PlotControls.vue new file mode 100644 index 0000000..37a97c2 --- /dev/null +++ b/src/components/util/PlotControls.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/components/util/plotMixins.js b/src/components/util/plotMixins.js new file mode 100644 index 0000000..5b9ceca --- /dev/null +++ b/src/components/util/plotMixins.js @@ -0,0 +1,19 @@ +export var plotZoomMixin = { + methods: { + plotZoom: function(zoomValue) { + var range = this.plot.getWindow(); + var interval = range.end - range.start; + + this.plot.setWindow({ + start: range.start.valueOf() - interval * zoomValue, + end: range.end.valueOf() + interval * zoomValue, + }); + }, + updateWindow: function(window) { + var currentWindow = this.plot.getWindow(); + if (currentWindow.start !== window.start || currentWindow.end !== window.end) { + this.plot.setWindow(window.start, window.end, { animation: false }); + } + }, + }, +}; diff --git a/src/views/Request.vue b/src/views/Request.vue index ee31100..6057c64 100644 --- a/src/views/Request.vue +++ b/src/views/Request.vue @@ -17,14 +17,12 @@ Sub-requests - - #{{ request.id }} - + #{{ request.id }} - + @@ -32,6 +30,7 @@ import $ from 'jquery'; import RequestgroupHeader from '@/components/RequestgroupHeader.vue'; +import RequestDetail from '@/components/RequestDetail.vue'; import RequestRow from '@/components/RequestRow.vue'; import NotFound from '@/views/NotFound.vue'; @@ -39,10 +38,11 @@ export default { name: 'Request', components: { RequestgroupHeader, + RequestDetail, RequestRow, - NotFound + NotFound, }, - data: function() { + data: function () { return { id: this.$route.params.id, requestgroup: {}, @@ -50,50 +50,53 @@ export default { instruments: {}, requestLoadingError: false, requestLoaded: false, - } + }; }, computed: { - observationPortalApiUrl: function() { + observationPortalApiUrl: function () { return this.$store.state.urls.observationPortalApi; - } + }, }, - created: function() { + created: function () { this.getRequest(); this.getInstruments(); }, methods: { - getInstruments: function() { + getInstruments: function () { let that = this; $.ajax({ url: this.observationPortalApiUrl + '/api/instruments/', - dataType: 'json' - }).done(function(response) { + dataType: 'json', + }).done(function (response) { that.instruments = response; - }) + }); }, - getRequest: function() { + getRequest: function () { let that = this; $.ajax({ url: this.observationPortalApiUrl + '/api/requestgroups/?request_id=' + this.id, - dataType: 'json' - }).done(function(response) { - let requestgroup = response.results; - if (requestgroup.length > 0) { - that.requestgroup = requestgroup[0]; - for (let request of requestgroup[0].requests) { - // TODO: Is there a better way to check this - if (String(request.id) === String(that.id)) { - that.request = request; - break; + dataType: 'json', + }) + .done(function (response) { + let requestgroup = response.results; + if (requestgroup.length > 0) { + that.requestgroup = requestgroup[0]; + for (let request of requestgroup[0].requests) { + // TODO: Is there a better way to check this + if (String(request.id) === String(that.id)) { + that.request = request; + break; + } } } - } - }).fail(function() { - that.requestLoadingError = true; - }).always(function() { - that.requestLoaded = true; - }) - } - } -} + }) + .fail(function () { + that.requestLoadingError = true; + }) + .always(function () { + that.requestLoaded = true; + }); + }, + }, +}; diff --git a/src/views/Requestgroup.vue b/src/views/Requestgroup.vue index b28b48c..317a6ff 100644 --- a/src/views/Requestgroup.vue +++ b/src/views/Requestgroup.vue @@ -4,7 +4,9 @@

Loading requestgroup