Skip to content

Commit cd34dfa

Browse files
committed
Merge remote-tracking branch 'origin/development'
2 parents a5dab61 + c8a62b8 commit cd34dfa

16 files changed

+325
-162
lines changed

src/components/DataTable.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export default {
8484
return this.canEdit(col) && this.editField != null && this.editField[0] == row && this.editField[1] == id;
8585
},
8686
onDblClick(event, row, col, id) {
87-
if (this.canEdit(col)) {
87+
if (!this.canEdit(col)) {
8888
return;
8989
}
9090

src/components/IDE.vue

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
<ServiceInfoModal ref="serviceModal" />
2929
<JobInfoModal ref="jobModal" />
3030
<ProcessGraphInfoModal ref="pgModal" />
31+
<ParameterModal ref="parameterModal" />
3132
</div>
3233
</template>
3334

@@ -46,6 +47,7 @@ import ServerInfoModal from './ServerInfoModal.vue';
4647
import JobInfoModal from './JobInfoModal.vue';
4748
import ProcessGraphInfoModal from './ProcessGraphInfoModal.vue';
4849
import ServiceInfoModal from './ServiceInfoModal.vue';
50+
import ParameterModal from './ParameterModal.vue';
4951
5052
export default {
5153
name: 'IDE',
@@ -60,7 +62,8 @@ export default {
6062
ServerInfoModal,
6163
JobInfoModal,
6264
ProcessGraphInfoModal,
63-
ServiceInfoModal
65+
ServiceInfoModal,
66+
ParameterModal
6467
},
6568
data() {
6669
return {
@@ -78,6 +81,7 @@ export default {
7881
EventBus.$on('showJobInfo', this.showJobInfo);
7982
EventBus.$on('showProcessGraphInfo', this.showProcessGraphInfo);
8083
EventBus.$on('showServiceInfo', this.showServiceInfo);
84+
EventBus.$on('showDataForm', this.showDataForm);
8185
EventBus.$on('getProcessGraph', this.getProcessGraph);
8286
EventBus.$on('insertProcessGraph', this.insertProcessGraph);
8387
this.updatePositions();
@@ -155,6 +159,11 @@ export default {
155159
156160
showWebEditorInfo() {
157161
EventBus.$emit('showWebEditorInfo');
162+
},
163+
164+
showDataForm(title, fields, saveCallback = null, closeCallback = null) {
165+
var editable = typeof saveCallback === 'function';
166+
this.$refs.parameterModal.show(title, fields, editable, saveCallback, closeCallback);
158167
}
159168
160169
}

src/components/JobPanel.vue

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
<button title="Details" @click="showJobInfo(p.row)" v-show="supports('describeJob')"><i class="fas fa-info"></i></button>
99
<button title="Show in Editor" @click="showInEditor(p.row)" v-show="supports('describeJob')"><i class="fas fa-code-branch"></i></button>
1010
<button title="Estimate" @click="estimateJob(p.row)" v-show="supports('estimateJob')"><i class="fas fa-file-invoice-dollar"></i></button>
11-
<button title="Update process graph" @click="updateProcessGraph(p.row)" v-show="supports('updateJob') && isJobInactive(p.row)"><i class="fas fa-edit"></i></button>
11+
<button title="Edit metadata" @click="editMetadata(p.row)" v-show="supports('updateJob') && isJobInactive(p.row)"><i class="fas fa-edit"></i></button>
12+
<button title="Replace process graph" @click="replaceProcessGraph(p.row)" v-show="supports('updateJob') && isJobInactive(p.row)"><i class="fas fa-retweet"></i></button>
1213
<button title="Delete" @click="deleteJob(p.row)" v-show="supports('deleteJob')"><i class="fas fa-trash"></i></button>
1314
<button title="Start processing" @click="queueJob(p.row)" v-show="supports('startJob') && isJobInactive(p.row)"><i class="fas fa-play-circle"></i></button>
1415
<button title="Cancel processing" @click="cancelJob(p.row)" v-show="supports('stopJob') && isJobActive(p.row)"><i class="fas fa-stop-circle"></i></button>
@@ -24,6 +25,7 @@
2425
import EventBus from '../eventbus.js';
2526
import WorkPanelMixin from './WorkPanelMixin.vue';
2627
import Utils from '../utils.js';
28+
import Field from './blocks/field';
2729
2830
export default {
2931
name: 'JobPanel',
@@ -115,25 +117,51 @@ export default {
115117
}
116118
Utils.confirm(this, 'Job created!', buttons);
117119
},
118-
createJob(processGraph, title = null) {
119-
this.connection.createJob(processGraph, title)
120+
getTitleField() {
121+
return new Field('title', 'Title', {type: 'string'});
122+
},
123+
getDescriptionField() {
124+
return new Field('description', 'Description', {type: 'string', format: 'commonmark'}, 'CommonMark (Markdown) is allowed.');
125+
},
126+
getBillingPlanField() {
127+
return new Field('plan', 'Billing plan', {type: 'string', format: 'billing-plan'});
128+
},
129+
getBudgetField() {
130+
return new Field('budget', 'Budget', {type: 'number', format: 'budget', default: null});
131+
},
132+
normalizeToDefaultData(data) {
133+
if (typeof data.title !== 'undefined' && (typeof data.title !== 'string' || data.title.length === 0)) {
134+
data.title = null;
135+
}
136+
if (typeof data.description !== 'undefined' && (typeof data.description !== 'string' || data.description.length === 0)) {
137+
data.description = null;
138+
}
139+
if (typeof data.plan !== 'undefined' && (typeof data.plan !== 'string' || data.plan.length === 0)) {
140+
data.plan = null;
141+
}
142+
if (typeof data.budget !== 'undefined' && (typeof data.budget !== 'number' || data.budget < 0)) {
143+
data.budget = null;
144+
}
145+
return data;
146+
},
147+
createJob(processGraph, data) {
148+
data = this.normalizeToDefaultData(data);
149+
this.connection.createJob(processGraph, data.title, data.description, data.plan, data.budget)
120150
.then(job => {
121151
EventBus.$emit('jobCreated', job);
122152
}).catch(error => {
123153
Utils.exception(this, error, 'Sorry, could not create a batch job.');
124154
});
125155
},
126156
createJobFromScript() {
127-
var title = prompt("Please specify a title for the job:");
128-
if (title === null) {
129-
return;
130-
}
131-
else if (typeof title !== 'string' || title.length === 0) {
132-
title = null;
133-
}
134-
135157
EventBus.$emit('getProcessGraph', script => {
136-
this.createJob(script, title);
158+
var fields = [
159+
this.getTitleField(),
160+
this.getDescriptionField(),
161+
this.getBillingPlanField(),
162+
this.getBudgetField()
163+
];
164+
EventBus.$emit('showDataForm', "Create new batch job", fields, data => this.createJob(script, data));
137165
});
138166
},
139167
updateJobData(updatedJob) {
@@ -190,23 +218,33 @@ export default {
190218
})
191219
.catch(error => Utils.exception(this, error, "Sorry, could not load job estimate."));
192220
},
193-
updateProcessGraph(job) {
221+
replaceProcessGraph(job) {
194222
EventBus.$emit('getProcessGraph', script => {
195-
job.updateJob(script)
196-
.then(updatedJob => {
197-
Utils.ok(this, "Job successfully updated.");
198-
this.updateJobData(updatedJob);
199-
})
200-
.catch(error => Utils.exception(this, error, "Sorry, could not update job."));;
223+
this.updateJob(job, {processGraph: script});
224+
});
225+
},
226+
editMetadata(oldJob) {
227+
this.refreshJob(oldJob, job => {
228+
var fields = [
229+
this.getTitleField().setValue(job.title),
230+
this.getDescriptionField().setValue(job.description),
231+
this.getBillingPlanField().setValue(job.plan),
232+
this.getBudgetField().setValue(job.budget)
233+
];
234+
EventBus.$emit('showDataForm', "Edit batch job", fields, data => this.updateJob(job, data));
201235
});
202236
},
203237
updateTitle(job, newTitle) {
204-
job.updateJob({title: newTitle})
238+
this.updateJob(job, {title: newTitle});
239+
},
240+
updateJob(job, data) {
241+
data = this.normalizeToDefaultData(data);
242+
job.updateJob(data)
205243
.then(updatedJob => {
206-
Utils.ok(this, "Job title successfully updated.");
244+
Utils.ok(this, "Job successfully updated.");
207245
this.updateJobData(updatedJob);
208246
})
209-
.catch(error => Utils.exception(this, error, "Sorry, could not update job title."));
247+
.catch(error => Utils.exception(this, error, "Sorry, could not update job."));;
210248
},
211249
queueJob(job) {
212250
job.startJob()

src/components/ParameterField.vue

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
<template>
22
<div class="fieldEditorContainer">
3-
<select class="fieldValue" v-if="schema.isEnum()" :name="fieldName" v-model="value" :disabled="!editable">
3+
<select class="fieldValue" v-if="schema.isEnum()" :name="fieldName" v-model="value" ref="selectFirst" :disabled="!editable">
44
<option v-for="(choice, k) in schema.getEnumChoices()" :key="k" :value="choice">{{ choice }}</option>
55
</select>
6-
<select class="fieldValue" v-else-if="type === 'collection-id'" :name="fieldName" v-model="value" :disabled="!editable">
6+
<select class="fieldValue" v-else-if="type === 'collection-id'" :name="fieldName" v-model="value" ref="selectFirst" :disabled="!editable">
77
<option v-for="c in collections" :key="c.id" :value="c.id">{{ c.id }}</option>
88
</select>
9-
<select class="fieldValue" v-else-if="type === 'output-format'" :name="fieldName" v-model="value" :disabled="!editable">
9+
<select class="fieldValue" v-else-if="type === 'output-format'" :name="fieldName" v-model="value" ref="selectFirst" :disabled="!editable">
1010
<option v-for="(x, format) in outputFormats" :key="format" :value="format.toUpperCase()">{{ format.toUpperCase() }}</option>
1111
</select>
12+
<select class="fieldValue" v-else-if="type === 'service-type'" :name="fieldName" v-model="value" ref="selectFirst" :disabled="!editable">
13+
<option v-for="(x, type) in serviceTypes" :key="type" :value="type.toUpperCase()">{{ type.toUpperCase() }}</option>
14+
</select>
15+
<select class="fieldValue" v-else-if="type === 'billing-plan'" :name="fieldName" v-model="value" ref="selectFirst" :disabled="!editable">
16+
<option v-for="plan in capabilities.listPlans()" :key="plan.name" :value="plan.name">{{ plan.name }} ({{ plan.paid ? 'paid' : 'free' }})</option>
17+
<!-- ToDo: Select the default plan if no value is set -->
18+
</select>
1219
<template v-else-if="type === 'temporal-interval'">
1320
<VueCtkDateTimePicker v-model="value" :disabled="!editable" :range="true" label="Select start and end time" format="YYYY-MM-DD[T]HH:mm:ss[Z]"></VueCtkDateTimePicker>
1421
<!-- ToDo: Support open date ranges, probably by using two separate date pickers, see also https://github.com/chronotruck/vue-ctk-date-time-picker/issues/121 -->
@@ -43,7 +50,12 @@
4350
</draggable>
4451
<button type="button" v-if="editable" @click="addField()"><i class="fas fa-plus"></i> Add</button>
4552
</div>
46-
<textarea class="fieldValue textarea" v-else-if="useTextarea" v-model="value" :disabled="!editable"></textarea>
53+
<template v-else-if="type === 'budget'">
54+
<input type="checkbox" v-model="hasBudget" value="1" />
55+
<input type="number" min="0.00" step="0.01" :disabled="!hasBudget || !editable" :name="fieldName" v-model="value" />&nbsp;{{ capabilities.currency() }}
56+
<!-- ToDo: Set max value of the input to the maximum budget available -->
57+
</template>
58+
<textarea class="fieldValue textarea" v-else-if="useTextarea" :name="fieldName" v-model="value" :disabled="!editable"></textarea>
4759
<input class="fieldValue" v-else-if="type === 'boolean'" :checked="!!value" v-model="value" type="checkbox" :name="fieldName" :disabled="!editable" />
4860
<input class="fieldValue" v-else v-model="value" type="text" :name="fieldName" :disabled="!editable" />
4961
</div>
@@ -88,16 +100,18 @@ export default {
88100
},
89101
data() {
90102
return {
91-
value: null
103+
value: null,
104+
hasBudget: false
92105
}
93106
},
94107
computed: {
95-
...Utils.mapState('server', ['collections', 'outputFormats']),
108+
...Utils.mapState('server', ['collections', 'outputFormats', 'serviceTypes']),
109+
...Utils.mapGetters('server', ['capabilities']),
96110
type() {
97111
return this.isItem ? this.schema.arrayOf() : this.schema.dataType();
98112
},
99113
useTextarea() {
100-
return (this.type === 'geojson' || this.type === 'proj-definition' || this.type === 'output-format-options' || this.type === 'process-graph-variables');
114+
return (this.type === 'geojson' || this.type === 'proj-definition' || this.type === 'output-format-options' || this.type === "service-type-parameters" || this.type === 'process-graph-variables' || this.type === 'commonmark');
101115
},
102116
fieldName() {
103117
return this.field.name + (Array.isArray(this.field.value) ? '[]' : '');
@@ -149,6 +163,11 @@ export default {
149163
map.dragging.disable();
150164
}
151165
}
166+
if (this.$refs.selectFirst && this.$refs.selectFirst.selectedOptions.length === 0) {
167+
this.$refs.selectFirst.selectedIndex = 0;
168+
// selectedIndex doesn't fire a v-model change, so set the value manually.
169+
this.value = this.$refs.selectFirst.value;
170+
}
152171
},
153172
initValue() {
154173
var v;
@@ -163,7 +182,7 @@ export default {
163182
v = "";
164183
}
165184
}
166-
else if (this.type === 'output-format') {
185+
else if (this.type === 'output-format' || this.type === 'service-type') {
167186
v = typeof this.$props.pass === 'string' ? this.$props.pass.toUpperCase() : this.$props.pass;
168187
}
169188
else if (this.type === 'callback') {
@@ -179,6 +198,10 @@ export default {
179198
v = null;
180199
}
181200
}
201+
else if (this.type === 'budget') {
202+
v = this.$props.pass;
203+
this.hasBudget = typeof v === 'number';
204+
}
182205
else if (this.type === 'array' || this.type === 'temporal-intervals') {
183206
v = [];
184207
if (Array.isArray(this.$props.pass)) {
@@ -192,7 +215,7 @@ export default {
192215
}
193216
else if (this.useTextarea) {
194217
if (typeof this.$props.pass === 'object') {
195-
if (Utils.size() > 0) {
218+
if (typeof this.$props.pass === 'object' && this.$props.pass !== null) {
196219
v = JSON.stringify(this.$props.pass, null, 2);
197220
}
198221
else {
@@ -236,18 +259,25 @@ export default {
236259
}
237260
return values;
238261
}
239-
else if (this.type === 'number') {
240-
return Number.parseFloat(this.value);
262+
else if (this.type === 'number' || this.type === 'budget') {
263+
var num = Number.parseFloat(this.value);
264+
return Number.isNaN(num) ? null : num;
241265
}
242266
else if (this.type === 'integer') {
243-
return Number.parseInt(this.value);
267+
var num = Number.parseInt(this.value);
268+
return Number.isNaN(num) ? null : num;
244269
}
245270
else if (this.type === 'null') {
246271
return null;
247272
}
248273
else if (this.useTextarea) {
249274
if (typeof this.value === 'string' && this.value.length > 0) {
250-
return JSON.parse(this.value);
275+
if (typeof this.$props.pass === 'object' && this.$props.pass !== null) {
276+
return JSON.parse(this.value);
277+
}
278+
else {
279+
return this.value;
280+
}
251281
}
252282
else {
253283
return null;

src/components/ParameterFields.vue

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ export default {
3939
if (this.field.schemas.length > 1) {
4040
JsonSchemaValidator.getTypeForValue(this.field.schemas.map(s => s.schema), this.pass)
4141
.then(type => {
42-
if (Array.isArray(type)) {
42+
if (typeof type === 'undefined') {
43+
this.guessType();
44+
}
45+
else if (Array.isArray(type)) {
4346
Utils.info("Data type can't be detected, please select it yourself.");
4447
console.log("Parameter schema is ambiguous. Potential types: " + type.join(', ') + ". Value: " + JSON.stringify(this.pass));
4548
this.type = type[0];
@@ -48,16 +51,25 @@ export default {
4851
this.type = type;
4952
}
5053
})
51-
.catch(error => {
52-
this.type = 0;
53-
});
54+
.catch(error => this.guessType());
5455
}
5556
else {
5657
this.type = 0;
5758
}
5859
},
5960
methods: {
60-
61+
guessType() {
62+
// Try to set null as default
63+
for(var i in this.field.schemas) {
64+
console.log(i, typeof this.field.schemas[i].isNull, this.field.schemas[i].isNull());
65+
if (this.field.schemas[i].isNull()) {
66+
this.type = i;
67+
return;
68+
}
69+
}
70+
// Otherwise set first type in list
71+
this.type = 0;
72+
},
6173
getValue() {
6274
return this.$refs.field.getValue();
6375
}

src/components/ParameterModal.vue

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
<template>
2-
<Modal ref="__modal">
2+
<Modal ref="__modal" minWidth="60%">
33
<template v-slot:main>
44
<p v-if="editableFields.length === 0">No editable parameters available.</p>
55
<form v-else id="parameterModal" @submit.prevent="save">
66
<div class="fieldRow" v-for="(field, k) in editableFields" :key="k">
77
<label>
8-
{{ field.label }}<strong class="required" v-if="field.isRequired()" title="required">*</strong>
9-
<div v-if="field.description()" class="description">
10-
<Description :description="field.description()" />
8+
{{ field.label }}<strong class="required" v-if="field.isRequired" title="required">*</strong>
9+
<div v-if="field.description" class="description">
10+
<Description :description="field.description" />
1111
</div>
1212
</label>
1313
<ParameterFields :ref="field.name" :editable="editable" :field="field" :pass="field.getValue()" />
@@ -115,6 +115,10 @@ export default {
115115
flex-grow: 1;
116116
width: 99%;
117117
}
118+
#parameterModal .fieldRow input[type="checkbox"].fieldValue {
119+
display: inline-block;
120+
flex-grow: unset;
121+
}
118122
119123
#parameterModal .description .styled-description {
120124
line-height: 1.1em;

0 commit comments

Comments
 (0)