blob: b1e5c5309478ed529d44b72fa4f1b7737e14f9b3 [file] [log] [blame] [view]
## Advanced Application Input Configuration
The Airavata Django Portal supports customization of the user interface used to
configure an application input. For example, instead of the default text input
box for a string input, with customization the UI can be configured to be a drop
down list of options.
To configure application inputs one needs to provide JSON configuration in the
**Advanced Input Field Modification Metadata** field of the application input.
1. **Settings** → **Application Catalog**. If not, click the gears icon on the left hand side.
3. Select the application you want to add advance input configurations.
4. Select the _Interface_ tab.
5. Scroll down to the _Input Field_ that you want to customize then scroll down to the **Advanced Input Field Modification Metadata** text box. This is the field where you will input your JSON configuration. This field will outline
in green when the JSON is valid and in red when invalid. See the following screenshot.
![Screenshot](../images/app-input-metadata.png)
Image: Advanced Input Field Modification Metadata
### Validation
**Example**
```json
{
"editor": {
"ui-component-id": "textarea-input-editor",
"config": {
"rows": 6
},
"validations": [
{
"type": "min-length",
"value": 10
},
{
"type": "max-length",
"value": 200
},
{
"type": "regex",
"value": "^[XYL\\s]+$",
"message": "Target sequence may only contain letters X, Y and L."
}
]
}
}
```
<br></br>
### Dependencies
You can hide/show inputs based on the values of other inputs. For example, if the option selected in input A is 'list-of-urls' then you can have input B show a field to upload a file.
**Example**
```json
{
"editor": {
"dependencies": {
"show": {
"Select reading options": {
"comparison": "equals",
"value": "list-urls"
}
}
}
}
}
```
## Alternate UI Components
### Checkboxes
**Example**
```json
{
"editor": {
"ui-component-id": "checkbox-input-editor",
"config": {
"options": [
{
"value": "a",
"text": "A label"
},
{
"value": "b",
"text": "B label"
},
{
"value": "c",
"text": "C label"
}
]
}
}
}
```
### Radio Buttons
**Example**
```json
{
"editor": {
"ui-component-id": "radio-button-input-editor",
"config": {
"options": [
{
"value": "breakfast",
"text": "Breakfast"
},
{
"value": "lunch",
"text": "Lunch"
},
{
"value": "dinner",
"text": "Dinner"
}
]
}
}
}
```
### Selects
**Example**
```json
{
"editor": {
"ui-component-id": "select-input-editor",
"config": {
"options": [
{
"value": "breakfast",
"text": "Breakfast"
},
{
"value": "lunch",
"text": "Lunch"
},
{
"value": "dinner",
"text": "Dinner"
}
]
}
}
}
```
### Slider
Displays a slider for picking a value between some minimum and maximum value.
#### Config options
- _min_ - minimum value, defaults to 0.
- _max_ - maximum value, defaults to 100.
- _step_ - step between values. Default is 1. Can be less that one for decimal
values.
- _valueFormat.percentage_ - whether to save the value with a trailing "%".
Defaults to false. Note, this is the value that will be placed in the job
script.
- _displayFormat.percentage_ - whether to display the value to the user with a
trailing "%". Defaults to false.
**Example**
```json
{
"editor": {
"ui-component-id": "slider-input-editor",
"config": {
"min": 32,
"max": 212,
"step": 0.1,
"valueFormat": {
"percentage": true
},
"displayFormat": {
"percentage": true
}
}
}
}
```
### Range Slider
Like the Slider component but there are two sliders, one for selecting the
beginning and one for selecting the end of a range.
#### Config options
All of the options of the Slider component, plus:
- _delimiter_ - the character to place between the values, for example, ",".
The default value is a hyphen, "-". This will be placed into the job script.
**Example**
```json
{
"editor": {
"ui-component-id": "range-slider-input-editor",
"config": {
"min": 32,
"max": 212,
"step": 0.1,
"valueFormat": {
"percentage": true
},
"displayFormat": {
"percentage": true
},
"delimiter": ","
}
}
}
```
### Autocomplete
The Autocomplete UI component looks up matching entries for the given substring
typed by the user. This one requires that a custom Django app be developed to
implement the REST API for returning autocomplete suggestions.
#### REST API contract
- URL is called with query parameter search with value of whatever the user
has currently typed
- URL should return a JSON response with a search key and the value of that
key used for the search and an results key with a list matching results,
limited to the top 10. Each result should have a text key with the text
displayed to the user and a value key which is the value applied to the
experiment input if selected. For example:
**Example**
```json
{
"search": "mammal",
"results": [
{
"text": "Horse",
"value": "horse"
},
{
"text": "Mouse",
"value": "mouse"
}
]
}
```
- URL can also be called with query parameter exact with a value that was
previously returned. This call is made by the UI to retrieve the "text"
value to display to the user for this value. The JSON response should be
similar to the above except that it should only have one result:
```json
{
"search": "horse",
"results": [
{
"text": "Horse",
"value": "horse"
}
]
}
```
- If the exact query parameter is specified and there is no match for that
value, the JSON response should have HTTP status 404. The error reason can
be added to the "detail" key of the response body, for example:
```json
{
"detail": "No matching value was found."
}
```
#### Example REST API implementation
To create the REST API backend needed by the Autocomplete component, first you
need a create a custom Django app. See
[Custom Django Apps](../dev/custom_django_app) for more information.
Here's a simple implementation of a view function that looks up words in the
system dictionary file:
```python
def test_autocomplete_search(request):
"""Find matching words for given search string."""
import re
if 'search' in request.GET:
query = request.GET['search']
pattern = re.compile(re.escape(query), re.IGNORECASE)
elif 'exact' in request.GET:
query = request.GET['exact']
pattern = re.compile(r"^" + re.escape(query) + r"$")
else:
return generic_json_exception_response(
"Missing required query parameter: one of 'search' or 'exact'", status=400)
matches = []
with open("/usr/share/dict/words", 'r') as f:
matches = [line.strip() for line in f if pattern.search(line)]
# TODO: if 'exact', make sure len(matches) == 1. if 0, then return 404
if 'exact' in request.GET and len(matches) == 0:
return generic_json_exception_response(f"No match for exact = {request.GET['exact']}", status=404)
return JsonResponse({
"search": query,
"results": [{"text": m, "value": m} for m in matches[:20]]
})
```
See also a real world example
[miga-autocomplete](https://github.com/bio-miga/miga-autocomplete).
#### Example Input Metadata Configuration
```json
{
"editor": {
"ui-component-id": "autocomplete-input-editor",
"config": {
"url": "/custom/search/"
}
}
}
```