diff --git a/modules/gui/src/api/tasks.js b/modules/gui/src/api/tasks.js index 9cdd319c26..d14d240b13 100644 --- a/modules/gui/src/api/tasks.js +++ b/modules/gui/src/api/tasks.js @@ -6,6 +6,14 @@ export default { retry }), + loadDetails$: taskId => + get$(`/api/tasks/task/${taskId}/details`), + + listExisting$: ({outputPath, destination, status}) => + get$('/api/tasks', { + query: {outputPath, destination, status} + }), + submit$: task => postJson$('/api/tasks', { body: task diff --git a/modules/gui/src/api/userFiles.js b/modules/gui/src/api/userFiles.js index 56cea14d5e..833f89bc60 100644 --- a/modules/gui/src/api/userFiles.js +++ b/modules/gui/src/api/userFiles.js @@ -1,6 +1,12 @@ +import {get$} from '~/http-client' + import {moduleWebSocket$} from './ws.js' export default { ws: () => moduleWebSocket$('user-files'), - downloadUrl: path => `/api/user-files/download?path=${encodeURIComponent(path)}` + downloadUrl: path => `/api/user-files/download?path=${encodeURIComponent(path)}`, + listFiles$: (path, options = {}) => + get$('/api/user-files/listFiles', { + query: {path, ...options}, + }), } diff --git a/modules/gui/src/app/home/body/process/recipe/asset/assetRecipe.js b/modules/gui/src/app/home/body/process/recipe/asset/assetRecipe.js index b3f544a4d3..0b2899abdf 100644 --- a/modules/gui/src/app/home/body/process/recipe/asset/assetRecipe.js +++ b/modules/gui/src/app/home/body/process/recipe/asset/assetRecipe.js @@ -1,12 +1,8 @@ import _ from 'lodash' import moment from 'moment' -import api from '~/apiRegistry' import {recipeActionBuilder} from '~/app/home/body/process/recipe' -import {getAllVisualizations} from '~/app/home/body/process/recipe/visualizations' -import {getRecipeType} from '~/app/home/body/process/recipeTypeRegistry' -import {publishEvent} from '~/eventPublisher' -import {msg} from '~/translate' +import {submitRetrieveRecipeTask as submitTask} from '~/app/home/body/process/recipe/recipeTaskSubmitter' const DATE_FORMAT = 'YYYY-MM-DD' @@ -77,42 +73,7 @@ export const RecipeActions = id => { } } -const submitRetrieveRecipeTask = recipe => { - const name = recipe.title || recipe.placeholder - const destination = recipe.ui.retrieveOptions.destination - const taskTitle = msg(['process.retrieve.form.task', destination], {name}) - const bands = recipe.ui.retrieveOptions.bands - const visualizations = getAllVisualizations(recipe) - const [timeStart, timeEnd] = (getRecipeType(recipe.type).getDateRange(recipe) || []).map(date => date.valueOf()) - const operation = `image.${destination}` - const recipeProperties = { - recipe_id: recipe.id, - recipe_projectId: recipe.projectId, - recipe_type: recipe.type, - recipe_title: recipe.title || recipe.placeholder, - ..._(recipe.model) - .mapValues(value => JSON.stringify(value)) - .mapKeys((_value, key) => `recipe_${key}`) - .value() - } - const task = { - operation, - params: { - title: taskTitle, - description: name, - image: { - recipe: _.omit(recipe, ['ui']), - ...recipe.ui.retrieveOptions, - bands: {selection: bands}, - visualizations, - properties: {...recipeProperties, 'system:time_start': timeStart, 'system:time_end': timeEnd} - } - } - } - publishEvent('submit_task', { - recipe_type: recipe.type, - destination, - data_set_type: 'RADAR' +const submitRetrieveRecipeTask = recipe => + submitTask(recipe, { + dataSetType: 'RADAR' }) - return api.tasks.submit$(task).subscribe() -} diff --git a/modules/gui/src/app/home/body/process/recipe/bandMath/bandMathRecipe.js b/modules/gui/src/app/home/body/process/recipe/bandMath/bandMathRecipe.js index ecfb09d1fe..6444048ad8 100644 --- a/modules/gui/src/app/home/body/process/recipe/bandMath/bandMathRecipe.js +++ b/modules/gui/src/app/home/body/process/recipe/bandMath/bandMathRecipe.js @@ -1,10 +1,5 @@ -import _ from 'lodash' - -import api from '~/apiRegistry' import {recipeActionBuilder} from '~/app/home/body/process/recipe' -import {getAllVisualizations} from '~/app/home/body/process/recipe/visualizations' -import {publishEvent} from '~/eventPublisher' -import {msg} from '~/translate' +import {submitRetrieveRecipeTask as submitTask} from '~/app/home/body/process/recipe/recipeTaskSubmitter' export const getDefaultModel = () => ({ inputImagery: {images: []}, @@ -32,40 +27,8 @@ export const RecipeActions = id => { } } -const submitRetrieveRecipeTask = recipe => { - const name = recipe.title || recipe.placeholder - const bands = recipe.ui.retrieveOptions.bands - const destination = recipe.ui.retrieveOptions.destination - const taskTitle = msg(['process.retrieve.form.task', destination], {name}) - const operation = `image.${destination}` - const recipeProperties = { - recipe_id: recipe.id, - recipe_projectId: recipe.projectId, - recipe_type: recipe.type, - recipe_title: recipe.title || recipe.placeholder, - ..._(recipe.model) - .mapValues(value => JSON.stringify(value)) - .mapKeys((_value, key) => `recipe_${key}`) - .value() - } - const task = { - operation, - params: { - title: taskTitle, - description: name, - image: { - ...recipe.ui.retrieveOptions, - recipe: _.omit(recipe, ['ui']), - bands: {selection: bands}, - visualizations: getAllVisualizations(recipe), - properties: recipeProperties, - } - } - } - publishEvent('submit_task', { - recipe_type: recipe.type, - destination +const submitRetrieveRecipeTask = recipe => + submitTask(recipe, { + includeTimeRange: false }) - return api.tasks.submit$(task).subscribe() -} diff --git a/modules/gui/src/app/home/body/process/recipe/baytsAlerts/baytsAlertsRecipe.js b/modules/gui/src/app/home/body/process/recipe/baytsAlerts/baytsAlertsRecipe.js index 2aa4e97f59..bab1794da7 100644 --- a/modules/gui/src/app/home/body/process/recipe/baytsAlerts/baytsAlertsRecipe.js +++ b/modules/gui/src/app/home/body/process/recipe/baytsAlerts/baytsAlertsRecipe.js @@ -1,13 +1,9 @@ -import _ from 'lodash' import moment from 'moment' -import api from '~/apiRegistry' import {recipeActionBuilder} from '~/app/home/body/process/recipe' import {defaultModel as defaultHistoricalModel} from '~/app/home/body/process/recipe/baytsHistorical/baytsHistoricalRecipe' -import {getRecipeType} from '~/app/home/body/process/recipeTypeRegistry' -import {publishEvent} from '~/eventPublisher' +import {pyramidingPolicies, submitRetrieveRecipeTask as submitTask} from '~/app/home/body/process/recipe/recipeTaskSubmitter' import {selectFrom} from '~/stateUtils' -import {msg} from '~/translate' import {visualizationOptions} from './visualizations' @@ -81,43 +77,7 @@ export const getAllVisualizations = recipe => { : [] } -const submitRetrieveRecipeTask = recipe => { - const name = recipe.title || recipe.placeholder - const destination = recipe.ui.retrieveOptions.destination - const taskTitle = msg(['process.retrieve.form.task', destination], {name}) - const bands = recipe.ui.retrieveOptions.bands - const visualizations = getAllVisualizations(recipe) - const [timeStart, timeEnd] = (getRecipeType(recipe.type).getDateRange(recipe) || []).map(date => date.valueOf()) - const pyramidingPolicy = {'.default': 'sample'} - const operation = `image.${destination}` - const recipeProperties = { - recipe_id: recipe.id, - recipe_projectId: recipe.projectId, - recipe_type: recipe.type, - recipe_title: recipe.title || recipe.placeholder, - ..._(recipe.model) - .mapValues(value => JSON.stringify(value)) - .mapKeys((_value, key) => `recipe_${key}`) - .value() - } - const task = { - operation, - params: { - title: taskTitle, - description: name, - image: { - recipe: _.omit(recipe, ['ui']), - ...recipe.ui.retrieveOptions, - bands: {selection: bands}, - visualizations, - pyramidingPolicy, - properties: {...recipeProperties, 'system:time_start': timeStart, 'system:time_end': timeEnd} - } - } - } - publishEvent('submit_task', { - recipe_type: recipe.type, - destination +const submitRetrieveRecipeTask = recipe => + submitTask(recipe, { + pyramidingPolicy: pyramidingPolicies.sample }) - return api.tasks.submit$(task).subscribe() -} diff --git a/modules/gui/src/app/home/body/process/recipe/baytsHistorical/baytsHistoricalRecipe.js b/modules/gui/src/app/home/body/process/recipe/baytsHistorical/baytsHistoricalRecipe.js index 001d96f134..62762a64a4 100644 --- a/modules/gui/src/app/home/body/process/recipe/baytsHistorical/baytsHistoricalRecipe.js +++ b/modules/gui/src/app/home/body/process/recipe/baytsHistorical/baytsHistoricalRecipe.js @@ -1,12 +1,7 @@ -import _ from 'lodash' import moment from 'moment' -import api from '~/apiRegistry' import {recipeActionBuilder} from '~/app/home/body/process/recipe' -import {getAllVisualizations} from '~/app/home/body/process/recipe/visualizations' -import {getRecipeType} from '~/app/home/body/process/recipeTypeRegistry' -import {publishEvent} from '~/eventPublisher' -import {msg} from '~/translate' +import {submitRetrieveRecipeTask as submitTask} from '~/app/home/body/process/recipe/recipeTaskSubmitter' import {getAvailableBands} from './bands' @@ -65,41 +60,9 @@ export const RecipeActions = id => { } const submitRetrieveRecipeTask = recipe => { - const name = recipe.title || recipe.placeholder - const destination = recipe.ui.retrieveOptions.destination - const taskTitle = msg(['process.retrieve.form.task', destination], {name}) const bands = Object.keys(getAvailableBands(recipe)) - const visualizations = getAllVisualizations(recipe) - const [timeStart, timeEnd] = (getRecipeType(recipe.type).getDateRange(recipe) || []).map(date => date.valueOf()) - const operation = `image.${destination}` - const recipeProperties = { - recipe_id: recipe.id, - recipe_projectId: recipe.projectId, - recipe_type: recipe.type, - recipe_title: recipe.title || recipe.placeholder, - ..._(recipe.model) - .mapValues(value => JSON.stringify(value)) - .mapKeys((_value, key) => `recipe_${key}`) - .value() - } - const task = { - operation, - params: { - title: taskTitle, - description: name, - image: { - recipe: _.omit(recipe, ['ui']), - ...recipe.ui.retrieveOptions, - bands: {selection: bands}, - visualizations, - properties: {...recipeProperties, 'system:time_start': timeStart, 'system:time_end': timeEnd} - } - } - } - publishEvent('submit_task', { - recipe_type: recipe.type, - destination, - data_set_type: 'RADAR' + return submitTask(recipe, { + dataSetType: 'RADAR', + customizeImage: image => ({...image, bands: {selection: bands}}) }) - return api.tasks.submit$(task).subscribe() } diff --git a/modules/gui/src/app/home/body/process/recipe/ccdc/ccdcRecipe.js b/modules/gui/src/app/home/body/process/recipe/ccdc/ccdcRecipe.js index 4dc81b634c..46a8a78fe5 100644 --- a/modules/gui/src/app/home/body/process/recipe/ccdc/ccdcRecipe.js +++ b/modules/gui/src/app/home/body/process/recipe/ccdc/ccdcRecipe.js @@ -6,6 +6,7 @@ import {recipeActionBuilder} from '~/app/home/body/process/recipe' import {defaultModel as defaultOpticalModel} from '~/app/home/body/process/recipe/opticalMosaic/opticalMosaicRecipe' import {defaultModel as defaultPlanetModel} from '~/app/home/body/process/recipe/planetMosaic/planetMosaicRecipe' import {defaultModel as defaultRadarModel} from '~/app/home/body/process/recipe/radarMosaic/radarMosaicRecipe' +import {getTaskInfo} from '~/app/home/body/process/recipe/recipeOutputPath' import {getAllVisualizations as recipeVisualizations} from '~/app/home/body/process/recipe/visualizations' import {getRecipeType} from '~/app/home/body/process/recipeTypeRegistry' import {publishEvent} from '~/eventPublisher' @@ -192,7 +193,9 @@ export const loadCCDCObservations$ = ({recipe, latLng, bands}) => const submitRetrieveRecipeTask = recipe => { const name = recipe.title || recipe.placeholder - const title = msg(['process.retrieve.form.task.GEE'], {name}) + const bands = recipe.ui.retrieveOptions.bands + const destination = 'GEE' + const taskTitle = msg(['process.retrieve.form.task.GEE'], {name}) const visualizations = getAllVisualizations(recipe) const [timeStart, timeEnd] = (getRecipeType(recipe.type).getDateRange(recipe) || []).map(date => date.valueOf()) const operation = 'ccdc.GEE' @@ -209,13 +212,20 @@ const submitRetrieveRecipeTask = recipe => { const task = { operation, params: { - title, + title: taskTitle, description: name, - recipe: _.omit(recipe, ['ui']), - ...recipe.ui.retrieveOptions, - bands: recipe.ui.retrieveOptions.bands, - visualizations, - properties: {...recipeProperties, 'system:time_start': timeStart, 'system:time_end': timeEnd} + image: { + ...recipe.ui.retrieveOptions, + recipe: _.omit(recipe, ['ui']), + bands, + visualizations, + properties: {...recipeProperties, 'system:time_start': timeStart, 'system:time_end': timeEnd} + }, + taskInfo: getTaskInfo({ + recipe, + destination, + retrieveOptions: recipe.ui.retrieveOptions + }) } } publishEvent('submit_task', { diff --git a/modules/gui/src/app/home/body/process/recipe/ccdcSlice/ccdcSliceRecipe.js b/modules/gui/src/app/home/body/process/recipe/ccdcSlice/ccdcSliceRecipe.js index 33423032f6..1f9fa5c901 100644 --- a/modules/gui/src/app/home/body/process/recipe/ccdcSlice/ccdcSliceRecipe.js +++ b/modules/gui/src/app/home/body/process/recipe/ccdcSlice/ccdcSliceRecipe.js @@ -4,11 +4,9 @@ import moment from 'moment' import api from '~/apiRegistry' import {recipeActionBuilder} from '~/app/home/body/process/recipe' import {toT} from '~/app/home/body/process/recipe/ccdc/t' -import {getRecipeType} from '~/app/home/body/process/recipeTypeRegistry' +import {submitRetrieveRecipeTask as submitTask} from '~/app/home/body/process/recipe/recipeTaskSubmitter' import {normalize} from '~/app/home/map/visParams/visParams' -import {publishEvent} from '~/eventPublisher' import {selectFrom} from '~/stateUtils' -import {msg} from '~/translate' export const defaultModel = { date: { @@ -105,9 +103,6 @@ export const additionalVisualizations = recipe => { } const submitRetrieveRecipeTask = recipe => { - const name = recipe.title || recipe.placeholder - const destination = recipe.ui.retrieveOptions.destination - const taskTitle = msg(['process.ccdcSlice.panel.retrieve.task', destination], {name}) const {baseBands, bandTypes, segmentBands} = recipe.ui.retrieveOptions const bandTypeSuffixes = { value: '', @@ -140,37 +135,12 @@ const submitRetrieveRecipeTask = recipe => { ...baseBands.map(({name}) => name), ...segmentBands ].filter(band => allBands.includes(band)) - const [timeStart, timeEnd] = (getRecipeType(recipe.type).getDateRange(recipe) || []).map(date => date.valueOf()) - const visualizations = getAllVisualizations(recipe) - .filter(({bands: visBands}) => visBands.every(band => bands.includes(band))) - const operation = `image.${destination}` - const recipeProperties = { - recipe_id: recipe.id, - recipe_projectId: recipe.projectId, - recipe_type: recipe.type, - recipe_title: recipe.title || recipe.placeholder, - ..._(recipe.model) - .mapValues(value => JSON.stringify(value)) - .mapKeys((_value, key) => `recipe_${key}`) - .value() - } - const task = { - operation, - params: { - title: taskTitle, - description: name, - image: { - recipe: _.omit(recipe, ['ui']), - ...recipe.ui.retrieveOptions, - bands: {selection: bands, baseBands}, - visualizations, - properties: {...recipeProperties, 'system:time_start': timeStart, 'system:time_end': timeEnd} - } - } - } - publishEvent('submit_task', { - recipe_type: recipe.type, - destination + + return submitTask(recipe, { + filterVisualizations: true, + customizeImage: image => ({ + ...image, + bands: {selection: bands, baseBands} + }) }) - return api.tasks.submit$(task).subscribe() } diff --git a/modules/gui/src/app/home/body/process/recipe/ccdcSlice/panels/retrieve/retrieve.jsx b/modules/gui/src/app/home/body/process/recipe/ccdcSlice/panels/retrieve/retrieve.jsx index cbac6e5dd8..32999c818c 100644 --- a/modules/gui/src/app/home/body/process/recipe/ccdcSlice/panels/retrieve/retrieve.jsx +++ b/modules/gui/src/app/home/body/process/recipe/ccdcSlice/panels/retrieve/retrieve.jsx @@ -84,18 +84,24 @@ const mapRecipeToProps = recipe => ({ }) class _Retrieve extends React.Component { - state = {more: false} - constructor(props) { super(props) + this.state = { + more: false, + destinationValidationPending: this.requiresDestinationValidation(props) + } const {recipeId, inputs: {scale}} = this.props this.recipeActions = RecipeActions(recipeId) if (!scale.value) scale.set(30) + this.onDestinationChange = this.onDestinationChange.bind(this) + this.onDestinationValidityCheckChange = this.onDestinationValidityCheckChange.bind(this) } render() { - const {more} = this.state + const {form} = this.props + const {more, destinationValidationPending} = this.state + const invalid = destinationValidationPending || form.isInvalid() return ( + applyLabel={msg('process.ccdcSlice.panel.retrieve.apply')} + invalid={invalid}>