This is an automated email from the ASF dual-hosted git repository. machristie pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git
commit e441f97c7a53ff5b11df5194bc66f5104bf69517 Author: Marcus Christie <[email protected]> AuthorDate: Tue Sep 4 16:00:52 2018 -0400 AIRAVATA-2876 Adding new application input --- .../applications/ApplicationInputFieldEditor.vue | 15 +- .../applications/ApplicationInterfaceEditor.vue | 18 +- .../api/static/django_airavata_api/js/index.js | 1 - .../js/models/ApplicationInterfaceDefinition.js | 7 - .../django_airavata_api/js/models/Experiment.js | 194 ++++++++-------- .../js/models/InputDataObjectType.js | 247 +++++++++++---------- 6 files changed, 262 insertions(+), 220 deletions(-) diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInputFieldEditor.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInputFieldEditor.vue index 085fa8b..65cd2a8 100644 --- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInputFieldEditor.vue +++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInputFieldEditor.vue @@ -1,7 +1,7 @@ <template> <b-card header="Input Field"> <b-form-group label="Name" :label-for="id+'-name'"> - <b-form-input :id="id+'-name'" type="text" v-model="data.name" required></b-form-input> + <b-form-input :id="id+'-name'" type="text" v-model="data.name" ref="nameInput" required></b-form-input> </b-form-group> <b-form-group label="Initial Value" :label-for="id+'-value'"> <b-form-input :id="id+'-value'" type="text" v-model="data.value"></b-form-input> @@ -55,6 +55,10 @@ export default { }, id: { required: true + }, + // Whether to put focus on the name field when mounting component + focus: { + type: Boolean } }, computed: { @@ -73,6 +77,15 @@ export default { methods: { copyValue() { return this.value.clone(); + }, + doFocus() { + this.$refs.nameInput.focus(); + this.$el.scrollIntoView(); + } + }, + mounted() { + if (this.focus) { + this.doFocus(); } } }; diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInterfaceEditor.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInterfaceEditor.vue index be490ba..00af581 100644 --- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInterfaceEditor.vue +++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationInterfaceEditor.vue @@ -26,7 +26,14 @@ <h1 class="h5 mb-4"> Input Fields </h1> - <application-input-field-editor v-for="(input, index) in data.applicationInputs" :value="input" :key="index" :id="'app-input-'+index" @input="updatedInput($event, index)" /> + <application-input-field-editor v-for="(input, index) in data.applicationInputs" :value="input" :key="index" :id="'app-input-'+index" :focus="index === focusApplicationInputIndex" @input="updatedInput($event, index)" /> + </div> + </div> + <div class="row"> + <div class="col"> + <b-button variant="secondary" @click="addApplicationInput"> + Add application input + </b-button> </div> </div> <div class="row"> @@ -63,6 +70,11 @@ export default { return [{ text: "True", value: true }, { text: "False", value: false }]; } }, + data() { + return { + focusApplicationInputIndex: null + }; + }, methods: { save() { this.$emit("save"); @@ -73,6 +85,10 @@ export default { updatedInput(newValue, index) { Object.assign(this.data.applicationInputs[index], newValue); this.$emit("input", this.data); + }, + addApplicationInput() { + this.data.applicationInputs.push(new models.InputDataObjectType()); + this.focusApplicationInputIndex = this.data.applicationInputs.length - 1; } } }; diff --git a/django_airavata/apps/api/static/django_airavata_api/js/index.js b/django_airavata/apps/api/static/django_airavata_api/js/index.js index 3d00054..ecb1e59 100644 --- a/django_airavata/apps/api/static/django_airavata_api/js/index.js +++ b/django_airavata/apps/api/static/django_airavata_api/js/index.js @@ -25,7 +25,6 @@ import SummaryType from './models/SummaryType' import UserPermission from './models/UserPermission' import ApplicationDeploymentService from './services/ApplicationDeploymentService' -import ApplicationInterfaceService from './services/ApplicationInterfaceService' import ExperimentService from './services/ExperimentService' import ExperimentSearchService from './services/ExperimentSearchService' import FullExperimentService from './services/FullExperimentService' diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationInterfaceDefinition.js b/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationInterfaceDefinition.js index 801ec52..43c3ba6 100644 --- a/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationInterfaceDefinition.js +++ b/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationInterfaceDefinition.js @@ -41,12 +41,5 @@ export default class ApplicationInterfaceDefinition extends BaseModel { constructor(data = {}) { super(FIELDS, data); - // Order application inputs - this.applicationInputs = this.getOrderedApplicationInputs(); - } - - getOrderedApplicationInputs() { - // Use slice() to make a copy and sort that copy - return this.applicationInputs ? this.applicationInputs.slice().sort((a, b) => a.inputOrder - b.inputOrder) : []; } } diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/Experiment.js b/django_airavata/apps/api/static/django_airavata_api/js/models/Experiment.js index a7f8304..47a49c4 100644 --- a/django_airavata/apps/api/static/django_airavata_api/js/models/Experiment.js +++ b/django_airavata/apps/api/static/django_airavata_api/js/models/Experiment.js @@ -9,111 +9,111 @@ import ProcessModel from './ProcessModel' import UserConfigurationData from './UserConfigurationData' const FIELDS = [ - 'experimentId', - 'projectId', - 'gatewayId', - { - name: 'experimentType', - type: 'number', - default: 0, - }, - 'userName', - 'experimentName', - { - name: 'creationTime', - type: 'date' - }, - 'description', - 'executionId', - { - name: 'enableEmailNotification', - type: 'boolean', - default: false, - }, - { - name: 'emailAddresses', - type: 'string', - list: true, - }, - { - name: 'userConfigurationData', - type: UserConfigurationData, - default: BaseModel.defaultNewInstance(UserConfigurationData), - }, - { - name: 'experimentInputs', - type: InputDataObjectType, - list: true, - }, - { - name: 'experimentOutputs', - type: OutputDataObjectType, - list: true, - }, - { - name: 'experimentStatus', - type: ExperimentStatus, - list: true, - }, - { - name: 'errors', - type: ErrorModel, - list: true, - }, - { - name: 'processes', - type: ProcessModel, - list: true, - }, + 'experimentId', + 'projectId', + 'gatewayId', + { + name: 'experimentType', + type: 'number', + default: 0, + }, + 'userName', + 'experimentName', + { + name: 'creationTime', + type: 'date' + }, + 'description', + 'executionId', + { + name: 'enableEmailNotification', + type: 'boolean', + default: false, + }, + { + name: 'emailAddresses', + type: 'string', + list: true, + }, + { + name: 'userConfigurationData', + type: UserConfigurationData, + default: BaseModel.defaultNewInstance(UserConfigurationData), + }, + { + name: 'experimentInputs', + type: InputDataObjectType, + list: true, + }, + { + name: 'experimentOutputs', + type: OutputDataObjectType, + list: true, + }, + { + name: 'experimentStatus', + type: ExperimentStatus, + list: true, + }, + { + name: 'errors', + type: ErrorModel, + list: true, + }, + { + name: 'processes', + type: ProcessModel, + list: true, + }, ]; export default class Experiment extends BaseModel { - constructor(data = {}) { - super(FIELDS, data); - } + constructor(data = {}) { + super(FIELDS, data); + } - validate() { - let validationResults = {}; - const userConfigurationDataValidation = this.userConfigurationData.validate(); - if (Object.keys(userConfigurationDataValidation).length > 0) { - validationResults['userConfigurationData'] = userConfigurationDataValidation; - } - if (this.isEmpty(this.experimentName)) { - validationResults['experimentName'] = "Please provide a name for this experiment."; - } - if (this.isEmpty(this.projectId)) { - validationResults['projectId'] = "Please select a project."; - } - return validationResults; + validate() { + let validationResults = {}; + const userConfigurationDataValidation = this.userConfigurationData.validate(); + if (Object.keys(userConfigurationDataValidation).length > 0) { + validationResults['userConfigurationData'] = userConfigurationDataValidation; } - - get latestStatus() { - if (this.experimentStatus && this.experimentStatus.length > 0) { - return this.experimentStatus[this.experimentStatus.length - 1]; - } else { - return null; - } + if (this.isEmpty(this.experimentName)) { + validationResults['experimentName'] = "Please provide a name for this experiment."; } - - get isProgressing() { - return this.latestStatus && this.latestStatus.isProgressing; + if (this.isEmpty(this.projectId)) { + validationResults['projectId'] = "Please select a project."; } + return validationResults; + } - get hasLaunched() { - const hasLaunchedStates = [ExperimentState.SCHEDULED, - ExperimentState.LAUNCHED, - ExperimentState.EXECUTING, - ExperimentState.CANCELING, - ExperimentState.CANCELED, - ExperimentState.FAILED, - ExperimentState.COMPLETED]; - return this.latestStatus - && hasLaunchedStates.indexOf(this.latestStatus.state) >= 0; + get latestStatus() { + if (this.experimentStatus && this.experimentStatus.length > 0) { + return this.experimentStatus[this.experimentStatus.length - 1]; + } else { + return null; } + } - populateInputsOutputsFromApplicationInterface(applicationInterface) { - // Copy application inputs and outputs to the experiment - this.experimentInputs = applicationInterface.getOrderedApplicationInputs().map(input => input.clone()); - this.experimentOutputs = applicationInterface.applicationOutputs.slice(); - } + get isProgressing() { + return this.latestStatus && this.latestStatus.isProgressing; + } + + get hasLaunched() { + const hasLaunchedStates = [ExperimentState.SCHEDULED, + ExperimentState.LAUNCHED, + ExperimentState.EXECUTING, + ExperimentState.CANCELING, + ExperimentState.CANCELED, + ExperimentState.FAILED, + ExperimentState.COMPLETED]; + return this.latestStatus + && hasLaunchedStates.indexOf(this.latestStatus.state) >= 0; + } + + populateInputsOutputsFromApplicationInterface(applicationInterface) { + // Copy application inputs and outputs to the experiment + this.experimentInputs = applicationInterface.applicationInputs.map(input => input.clone()); + this.experimentOutputs = applicationInterface.applicationOutputs.slice(); + } } diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/InputDataObjectType.js b/django_airavata/apps/api/static/django_airavata_api/js/models/InputDataObjectType.js index 06cd623..83bb1c7 100644 --- a/django_airavata/apps/api/static/django_airavata_api/js/models/InputDataObjectType.js +++ b/django_airavata/apps/api/static/django_airavata_api/js/models/InputDataObjectType.js @@ -4,130 +4,151 @@ import DataType from './DataType'; import ValidatorFactory from './validators/ValidatorFactory'; const FIELDS = [ - 'name', - 'value', - { - name: 'type', - type: DataType, - }, - 'applicationArgument', - 'standardInput', - 'userFriendlyDescription', - 'metaData', - 'inputOrder', - 'isRequired', - 'requiredToAddedToCommandLine', - 'dataStaged', - 'storageResourceId', - 'isReadOnly', + 'name', + 'value', + { + name: 'type', + type: DataType, + default: DataType.STRING, + }, + 'applicationArgument', + { + name: 'standardInput', + type: 'boolean', + default: false, + }, + 'userFriendlyDescription', + 'metaData', + 'inputOrder', + { + name: 'isRequired', + type: 'boolean', + default: false, + }, + { + name: 'requiredToAddedToCommandLine', + type: 'boolean', + default: false, + }, + { + name: 'dataStaged', + type: 'boolean', + default: false, + }, + 'storageResourceId', + { + name: 'isReadOnly', + type: 'boolean', + default: false, + }, ]; export default class InputDataObjectType extends BaseModel { - constructor(data = {}) { - super(FIELDS, data); - } + constructor(data = {}) { + super(FIELDS, data); + } - /** - * Get the UI component id for the editor component to use for this input. - * Returns null if there is no editor UI component id. - * - * The expected JSON schema for the editor UI component id is the following: - * { - * "editor": { - * "ui-component-id": "input-editor-component-id", - * ... - * } - * } - */ - get editorUIComponentId() { - const metadata = this._getMetadata(); - if (metadata && 'editor' in metadata && 'ui-component-id' in metadata['editor']) { - return metadata['editor']['ui-component-id']; - } else { - return null; - } + /** + * Get the UI component id for the editor component to use for this input. + * Returns null if there is no editor UI component id. + * + * The expected JSON schema for the editor UI component id is the following: + * { + * "editor": { + * "ui-component-id": "input-editor-component-id", + * ... + * } + * } + */ + get editorUIComponentId() { + const metadata = this._getMetadata(); + if (metadata && 'editor' in metadata && 'ui-component-id' in metadata['editor']) { + return metadata['editor']['ui-component-id']; + } else { + return null; } + } - /** - * Get the configuration for the editor component, which will be available - * to the editor component for customizing its look and functionality. - * Returns empty object if there is no editor config. - * - * The expected JSON schema for the editor config is the following: - * { - * "editor": { - * "config": { - * ... anything can go here ... - * } - * ... - * } - * } - */ - get editorConfig() { - const metadata = this._getMetadata(); - if (metadata && 'editor' in metadata && 'config' in metadata['editor']) { - return metadata['editor']['config']; - } else { - return {}; - } + /** + * Get the configuration for the editor component, which will be available + * to the editor component for customizing its look and functionality. + * Returns empty object if there is no editor config. + * + * The expected JSON schema for the editor config is the following: + * { + * "editor": { + * "config": { + * ... anything can go here ... + * } + * ... + * } + * } + */ + get editorConfig() { + const metadata = this._getMetadata(); + if (metadata && 'editor' in metadata && 'config' in metadata['editor']) { + return metadata['editor']['config']; + } else { + return {}; } + } - /** - * Get the validations for the editor component. See ValidatorFactory for a - * list of available validations. Returns empty array if there are no - * validations. - * - * The expected JSON schema for the editor validations is the following: - * { - * "editor": { - * "validations": [ - * { - * "type": "validation-name", - * "value": "some value for configuring validation, passed to validator constructor", - * "message": "(Optional) custom validation error message" - * }, - * ... additional validations go here ... - * ] - * ... - * } - * } - * - * Note: "message" is optional for all validations. - */ - get editorValidations() { - const metadata = this._getMetadata(); - if (metadata && 'editor' in metadata && 'validations' in metadata['editor']) { - return metadata['editor']['validations']; - } else { - return []; - } + /** + * Get the validations for the editor component. See ValidatorFactory for a + * list of available validations. Returns empty array if there are no + * validations. + * + * The expected JSON schema for the editor validations is the following: + * { + * "editor": { + * "validations": [ + * { + * "type": "validation-name", + * "value": "some value for configuring validation, passed to validator constructor", + * "message": "(Optional) custom validation error message" + * }, + * ... additional validations go here ... + * ] + * ... + * } + * } + * + * Note: "message" is optional for all validations. + */ + get editorValidations() { + const metadata = this._getMetadata(); + if (metadata && 'editor' in metadata && 'validations' in metadata['editor']) { + return metadata['editor']['validations']; + } else { + return []; } + } - _getMetadata() { - // metaData could really be anything, here we expect it to be an object - // so safely check if it is first - if (this.metaData && typeof this.metaData === 'object') { - return this.metaData; - } else { - return null; - } + _getMetadata() { + // metaData could really be anything, here we expect it to be an object + // so safely check if it is first + if (this.metaData && typeof this.metaData === 'object') { + return this.metaData; + } else { + return null; } + } - validate(experiment, value = undefined) { - let inputValue = typeof value != 'undefined' ? value : this.value; - let results = {}; - let valueErrorMessages = []; - if (this.isRequired && this.isEmpty(inputValue)) { - valueErrorMessages.push('This field is required.'); - } - // Run through any validations if configured - if (this.editorValidations.length > 0) { - const validatorFactory = new ValidatorFactory(); - valueErrorMessages = valueErrorMessages.concat(validatorFactory.validate(this.editorValidations, inputValue)); - } - if (valueErrorMessages.length > 0) { - results['value'] = valueErrorMessages; - } - return results; + validate(experiment, value = undefined) { + let inputValue = typeof value != 'undefined' ? value : this.value; + let results = {}; + let valueErrorMessages = []; + if (this.isRequired && this.isEmpty(inputValue)) { + valueErrorMessages.push('This field is required.'); + } + // Run through any validations if configured + if (this.editorValidations.length > 0) { + const validatorFactory = new ValidatorFactory(); + valueErrorMessages = valueErrorMessages.concat(validatorFactory.validate(this.editorValidations, inputValue)); + } + if (valueErrorMessages.length > 0) { + results['value'] = valueErrorMessages; } + return results; + } }
