Commit e147c5cf authored by Joern jkuensem's avatar Joern jkuensem Committed by Jorrit Schaap

TMSS-145: Add JSON editor widget to TaskDraft and Subtask DRF form view, some cleanup

parent 38b37529
<!-- EXTERNAL RESOURCES -->
<!--<link rel="stylesheet" id="theme" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">-->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script src="https://unpkg.com/react-jsonschema-form/dist/react-jsonschema-form.js"></script>
<!-- LOGIC -->
<script type="text/babel">
const { Component } = React;
const { render } = ReactDOM;
const {default: Form} = JSONSchemaForm;
const log = (type) => console.log.bind(console, type);
// Read current document provided by Django. Change 'null' default to {}
var formData = {{ field.value|safe }};
if (formData === "null" || formData === null){
var formData = {}
}
console.log("Initial data: " + JSON.stringify(formData));
// Updates the hidden input that Django will read the modified JSON from
function set_return_doc(doc){
document.getElementById("helper_input").value = JSON.stringify(doc.formData);
// console.log(JSON.stringify(doc.formData));
}
// Default live validation looks pretty nice in the demo, does not show here for some reason.
// Custom validation function works as such:
function validate(formData, errors) {
// todo: check formData against schema by some external script or so
// if(valid) (
errors.relative_starttime.addError("The horror!");
// }
return errors;
}
// This let's you change the appearance of the individual fields.
// Haven't found a global switch for fields of certain type yet.
const uiSchema = {
relative_starttime: { "ui:widget": "range" }
};
// Read schema uri that was specified by Serializer
const template_schema_uri = "{{field.style.template_schema_uri}}";
console.log('Loading template from ' + template_schema_uri);
// Get the schema, then render the form with it
$.get(template_schema_uri, function( template ) {
const schema = template['schema'];
console.log('Schema is: ' + JSON.stringify(schema));
console.log('Rendering JSON form');
render((
<Form schema={schema}
uiSchema={uiSchema}
formData={formData}
liveValidate={true}
//validate={validate} // does not work?!
onChange={set_return_doc}
onSubmit={log("submitted")}
onError={log("errors")} />
), document.getElementById("app"));
});
console.log('Rendering done');
</script>
<!-- WIDGET HTML -->
<div class="form-group {% if field.errors %}has-error{% endif %}">
<label class="col-sm-2 control-label">
{% if field.label %}{{ field.label }}{% endif %}
</label>
<input id="helper_input" name="{{ field.name }}" type="hidden" class="form-control" {% if field.value != None %} value="{{ field.value|safe }}"{% endif %}">
<div class="col-sm-10">
<div id="app"></div>
</div>
<!--<pre> {% debug %} </pre>-->
</div>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<script src="https://cdn.jsdelivr.net/npm/@json-editor/json-editor/dist/jsoneditor.min.js"></script>
</head>
<body>
<div id='app'></div>
<script>
var element = document.getElementById('app');
var schema = {
"type": "array",
"title": "Numbers",
"items": {
"type": "number",
"format": "number",
"title": "Number"
}
};
var editor = new JSONEditor(element, {
schema: schema
});
document.querySelector('.get-value').addEventListener('click', function () {
debug.value = JSON.stringify(editor.getValue());
});
</script>
</body>
</html>
<!-- EXTERNAL RESOURCES -->
<!--<link rel="stylesheet" id="theme" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">-->
<!--<script src="https://unpkg.com/react@16/umd/react.development.js"></script>-->
<!--<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>-->
<!--<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>-->
<script src="https://cdn.jsdelivr.net/npm/@json-editor/json-editor/dist/jsoneditor.min.js"></script>
<!-- WIDGET HTML -->
<div class="form-group {% if field.errors %}has-error{% endif %}">
<label class="col-sm-2 control-label">
{% if field.label %}{{ field.label }}{% endif %}
</label>
<input id="helper_input" name="{{ field.name }}" type="hidden" class="form-control" {% if field.value != None %} value="{{ field.value|safe }}"{% endif %}">
<div class="col-sm-10">
<div id="app"></div>
</div>
<!--<pre> {% debug %} </pre>-->
</div>
<!-- LOGIC -->
<script type="text/javascript">
// Read current document provided by Django. Change 'null' default to {}
var formData = {{ field.value|safe }};
if (formData === "null" || formData === null){
var formData = {}
}
console.log("Initial data: " + JSON.stringify(formData));
// Read schema (added to widget style by Serializer)
var schema = {{ field.style.schema|safe }};
console.log('Schema is: ' + JSON.stringify(schema));
// render the widget
console.log('Rendering JSON form');
JSONEditor.defaults.options.theme = 'bootstrap3';
JSONEditor.defaults.options.iconlib = "bootstrap3";
var element = document.getElementById('app');
var editor = new JSONEditor(element,{
schema: schema
});
editor.setValue(formData);
console.log('Rendering done');
// Updates the hidden input that Django will read the modified JSON from
editor.on('change',function() {
var value = editor.getValue();
document.getElementById("helper_input").value = JSON.stringify(value);
//console.log(JSON.stringify(value));
});
console.log(value)
</script>
This diff is collapsed.
<!-- EXTERNAL RESOURCES -->
<!--<link rel="stylesheet" id="theme" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">-->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script src="https://unpkg.com/react-jsonschema-form/dist/react-jsonschema-form.js"></script>
<!-- LOGIC -->
<script type="text/babel">
const { Component } = React;
const { render } = ReactDOM;
const {default: Form} = JSONSchemaForm;
const log = (type) => console.log.bind(console, type);
// Read current document provided by Django. Change 'null' default to {}
var formData = {{ field.value|safe }};
if (formData === "null" || formData === null){
var formData = {}
}
console.log("Initial data: " + JSON.stringify(formData));
// Updates the hidden input that Django will read the modified JSON from
function set_return_doc(doc){
document.getElementById("helper_input").value = JSON.stringify(doc.formData);
// console.log(JSON.stringify(doc.formData));
}
// Default live validation looks pretty nice in the demo, does not show here for some reason.
// Custom validation function works as such:
function validate(formData, errors) {
// todo: check formData against schema by some external script or so
// if(valid) (
errors.relative_starttime.addError("The horror!");
// }
return errors;
}
// This let's you change the appearance of the individual fields.
// Haven't found a global switch for fields of certain type yet.
const uiSchema = {
relative_starttime: { "ui:widget": "range" }
};
// Read schema (added to widget style by Serializer)
const schema = {{ field.style.schema|safe }};
console.log('Schema is: ' + JSON.stringify(schema));
//Render the form
console.log('Rendering JSON form');
render((
<Form schema={schema}
uiSchema={uiSchema}
formData={formData}
liveValidate={true}
//validate={validate} // does not work?!
onChange={set_return_doc}
onSubmit={log("submitted")}
onError={log("errors")} />
), document.getElementById("app"));
console.log('Rendering done');
</script>
<!-- WIDGET HTML -->
<div class="form-group {% if field.errors %}has-error{% endif %}">
<label class="col-sm-2 control-label">
{% if field.label %}{{ field.label }}{% endif %}
</label>
<input id="helper_input" name="{{ field.name }}" type="hidden" class="form-control" {% if field.value != None %} value="{{ field.value|safe }}"{% endif %}">
<div class="col-sm-10">
<div id="app"></div>
</div>
<!--<pre> {% debug %} </pre>-->
</div>
\ No newline at end of file
......@@ -6,7 +6,11 @@ from ..populate import *
class Migration(migrations.Migration):
dependencies = [
<<<<<<< HEAD:SAS/TMSS/src/tmss/tmssapp/migrations/0003_populate.py
('tmssapp', '0002_auto_20200204_1117'),
=======
('tmssapp', '0003_auto_20200210_1715'),
>>>>>>> TMSS-145: Add JSON editor widget to TaskDraft and Subtask DRF form view, some cleanup:SAS/TMSS/src/tmss/tmssapp/migrations/0004_populate.py
]
operations = [ migrations.RunPython(populate_choices),
......
......@@ -6,7 +6,6 @@ from django.db.models import Model, CharField, DateTimeField, BooleanField, Fore
from django.contrib.postgres.fields import ArrayField, JSONField
from django.contrib.postgres.indexes import GinIndex
from enum import Enum
from django_json_widget.widgets import JSONEditorWidget
#
# Common
......@@ -144,9 +143,6 @@ class TaskConnectors(BasicCommon):
# abstract models
class Template(NamedCommon):
formfield_overrides = {
JSONField: {'widget': JSONEditorWidget},
}
version = CharField(max_length=128, help_text='Version of this template (with respect to other templates of the same name).')
schema = JSONField(help_text='Schema for the configurable parameters needed to use this template.')
......
......@@ -137,3 +137,32 @@ class DataproductHashSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.DataproductHash
fields = '__all__'
class SubtaskSerializerJSONeditorOnline(RelationalHyperlinkedModelSerializer):
# Create a JSON editor form to replace the simple text field based on the schema in the template that this
# draft refers to. If that fails, the JSONField remains a standard text input.
#
# Note: I feel a bit uneasy with this since I feel there should be a more straight-forward solution tham
# ...intercepting the init process to determine the schema (or template uri or so) for the style attribute.
# ...Hoewever, I did not manage to simply pass the value(!) of e.g. the template field as a style attribute
# ...of the JSONField via a SerializerMethodField or similar, although I feel that should be possible.
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
import json
try:
schema = self.instance.specifications_template.schema
self.fields['specifications_doc'] = serializers.JSONField(
style={'template': 'josdejong_jsoneditor_widget.html',
'schema': json.dumps(schema)})
except:
# todo: Shall we use one of the default templates for the init?
print('Could not determine schema, hence no fancy JSON form. Expected for list view.')
class Meta:
model = models.Subtask
fields = '__all__'
\ No newline at end of file
......@@ -163,8 +163,7 @@ class TaskRelationBlueprintSerializer(serializers.HyperlinkedModelSerializer):
# ----- JSON
class TaskBlueprintSerializerReactJSONform(RelationalHyperlinkedModelSerializer):
class TaskBlueprintSerializerJSONeditorOnline(RelationalHyperlinkedModelSerializer):
# Create a JSON editor form to replace the simple text field based on the schema in the template that this
# blueprint refers to. If that fails, the JSONField remains a standard text input.
......@@ -179,57 +178,25 @@ class TaskBlueprintSerializerReactJSONform(RelationalHyperlinkedModelSerializer)
import json
try:
schema = self.instance.template.schema
self.fields['requirements_doc'] = serializers.JSONField(
style={'template': 'react_jsonschema_form_widget.html',
schema = self.instance.specifications_template.schema
self.fields['specifications_doc'] = serializers.JSONField(
style={'template': 'josdejong_jsoneditor_widget.html',
'schema': json.dumps(schema)})
except:
# todo: Shall we use one of the default templates for the init?
print('Could not determine schema, hence no fancy JSON form. Expected for list view.')
class Meta:
model = models.TaskBlueprint
fields = '__all__'
extra_fields = ['subtasks', 'produced_by', 'consumed_by']
class TaskBlueprintSerializerJSONeditor(RelationalHyperlinkedModelSerializer):
# Create a JSON editor form to replace the simple text field based on the schema in the template that this
# blueprint refers to. If that fails, the JSONField remains a standard text input.
#
# Note: I feel a bit uneasy with this since I feel there should be a more straight-forward solution tham
# ...intercepting the init process to determine the schema (or template uri or so) for the style attribute.
# ...Hoewever, I did not manage to simply pass the value(!) of e.g. the template field as a style attribute
# ...of the JSONField via a SerializerMethodField or similar, although I feel that should be possible.
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
import json
try:
schema = self.instance.template.schema
self.fields['requirements_doc'] = serializers.JSONField(
style={'template': 'json_editor_widget.html',
'schema': json.dumps(schema)})
except:
# todo: Shall we use one of the default templates for the init?
print('Could not determine schema, hence no fancy JSON form. Expected for list view.')
#requirements_doc = serializers.JSONField(style={'template': 'json_editor_form_2.html', 'template_schema_uri': "http://localhost:8000/task_template/2/?format=json"})
class Meta:
model = models.TaskBlueprint
fields = '__all__'
extra_fields = ['subtasks', 'produced_by', 'consumed_by']
class TaskBlueprintSerializerJSONeditorOnline(RelationalHyperlinkedModelSerializer):
class TaskDraftSerializerJSONeditorOnline(RelationalHyperlinkedModelSerializer):
# Create a JSON editor form to replace the simple text field based on the schema in the template that this
# blueprint refers to. If that fails, the JSONField remains a standard text input.
# draft refers to. If that fails, the JSONField remains a standard text input.
#
# Note: I feel a bit uneasy with this since I feel there should be a more straight-forward solution tham
# ...intercepting the init process to determine the schema (or template uri or so) for the style attribute.
......@@ -241,8 +208,8 @@ class TaskBlueprintSerializerJSONeditorOnline(RelationalHyperlinkedModelSerializ
import json
try:
schema = self.instance.template.schema
self.fields['requirements_doc'] = serializers.JSONField(
schema = self.instance.specifications_template.schema
self.fields['specifications_doc'] = serializers.JSONField(
style={'template': 'josdejong_jsoneditor_widget.html',
'schema': json.dumps(schema)})
......@@ -250,17 +217,9 @@ class TaskBlueprintSerializerJSONeditorOnline(RelationalHyperlinkedModelSerializ
# todo: Shall we use one of the default templates for the init?
print('Could not determine schema, hence no fancy JSON form. Expected for list view.')
#requirements_doc = serializers.JSONField(style={'template': 'json_editor_form_2.html', 'template_schema_uri': "http://localhost:8000/task_template/2/?format=json"})
class Meta:
model = models.TaskBlueprint
model = models.TaskDraft
fields = '__all__'
extra_fields = ['subtasks', 'produced_by', 'consumed_by']
extra_fields = ['related_task_blueprint', 'produced_by', 'consumed_by']
class TaskBlueprintJSONSerializer(RelationalHyperlinkedModelSerializer):
class Meta:
model = models.TaskBlueprint
fields = ('requirements_doc',)
extra_fields = ['subtasks', 'produced_by', 'consumed_by']
\ No newline at end of file
......@@ -132,3 +132,17 @@ class DataproductArchiveInfoViewSet(LOFARViewSet):
class DataproductHashViewSet(LOFARViewSet):
queryset = models.DataproductHash.objects.all()
serializer_class = serializers.DataproductHashSerializer
# --- JSON
class SubtaskViewSetJSONeditorOnline(LOFARViewSet):
queryset = models.Subtask.objects.all()
serializer_class = serializers.SubtaskSerializerJSONeditorOnline
def get_queryset(self):
if 'task_blueprint_pk' in self.kwargs:
task_blueprint = get_object_or_404(models.TaskBlueprint, pk=self.kwargs['task_blueprint_pk'])
return task_blueprint.subtasks.all()
else:
return models.Subtask.objects.all()
......@@ -128,15 +128,15 @@ class SchedulingUnitBlueprintViewSet(LOFARViewSet):
class TaskDraftViewSet(LOFARViewSet):
queryset = models.TaskDraft.objects.all()
serializer_class = serializers.TaskDraftSerializer
queryset = models.TaskDraft.objects.all()
serializer_class = serializers.TaskDraftSerializer
def get_queryset(self):
if 'scheduling_unit_draft_pk' in self.kwargs:
scheduling_unit_draft = get_object_or_404(models.SchedulingUnitDraft, pk=self.kwargs['scheduling_unit_draft_pk'])
return scheduling_unit_draft.task_drafts.all()
else:
return models.TaskDraft.objects.all()
def get_queryset(self):
if 'scheduling_unit_draft_pk' in self.kwargs:
scheduling_unit_draft = get_object_or_404(models.SchedulingUnitDraft, pk=self.kwargs['scheduling_unit_draft_pk'])
return scheduling_unit_draft.task_drafts.all()
else:
return models.TaskDraft.objects.all()
class TaskBlueprintViewSet(LOFARViewSet):
......@@ -181,27 +181,18 @@ class TaskRelationBlueprintViewSet(LOFARViewSet):
# --- JSON
class TaskBlueprintViewSetReactJSONform(LOFARViewSet):
queryset = models.TaskBlueprint.objects.all()
serializer_class = serializers.TaskBlueprintSerializerReactJSONform
class TaskBlueprintViewSetJSONeditor(LOFARViewSet):
queryset = models.TaskBlueprint.objects.all()
serializer_class = serializers.TaskBlueprintSerializerJSONeditor
class TaskBlueprintViewSetJSONeditorOnline(LOFARViewSet):
queryset = models.TaskBlueprint.objects.all()
serializer_class = serializers.TaskBlueprintSerializerJSONeditorOnline
queryset = models.TaskBlueprint.objects.all()
serializer_class = serializers.TaskBlueprintSerializerJSONeditorOnline
# # todo: this is experimental / for demo purposes. Remove or make functional.
# class JSONEditorViewSet(LOFARViewSet):
# renderer_classes = [TemplateHTMLRenderer]
# template_name = 'react_jsonschema_form.html'
# queryset = models.TaskBlueprint.objects.all()
# serializer_class = serializers.TaskBlueprintJSONSerializer
class TaskDraftViewSetJSONeditorOnline(LOFARViewSet):
queryset = models.TaskDraft.objects.all()
serializer_class = serializers.TaskDraftSerializerJSONeditorOnline
def get_queryset(self):
if 'scheduling_unit_draft_pk' in self.kwargs:
scheduling_unit_draft = get_object_or_404(models.SchedulingUnitDraft, pk=self.kwargs['scheduling_unit_draft_pk'])
return scheduling_unit_draft.task_drafts.all()
else:
return models.TaskDraft.objects.all()
\ No newline at end of file
......@@ -88,7 +88,7 @@ router.register(r'project', viewsets.ProjectViewSet)
router.register(r'scheduling_set', viewsets.SchedulingSetViewSet)
router.register(r'scheduling_unit_draft', viewsets.SchedulingUnitDraftViewSet)
router.register(r'scheduling_unit_blueprint', viewsets.SchedulingUnitBlueprintViewSet)
router.register(r'task_draft', viewsets.TaskDraftViewSet)
#router.register(r'task_draft', viewsets.TaskDraftViewSet) # todo: default view, re-activate or remove the JSON editor one in bottom
router.register(r'task_blueprint', viewsets.TaskBlueprintViewSet)
router.register(r'task_relation_draft', viewsets.TaskRelationDraftViewSet)
router.register(r'task_relation_blueprint', viewsets.TaskRelationBlueprintViewSet)
......@@ -97,7 +97,7 @@ router.register(r'task_relation_blueprint', viewsets.TaskRelationBlueprintViewSe
router.register(r'cycle/(?P<cycle_pk>[\w\-]+)/project', viewsets.ProjectViewSet)
router.register(r'scheduling_set/(?P<scheduling_set_pk>\d+)/scheduling_unit_draft', viewsets.SchedulingUnitDraftViewSet)
router.register(r'scheduling_unit_draft/(?P<scheduling_unit_draft_pk>\d+)/scheduling_unit_blueprint', viewsets.SchedulingUnitBlueprintViewSet)
router.register(r'scheduling_unit_draft/(?P<scheduling_unit_draft_pk>\d+)/task_draft', viewsets.TaskDraftViewSet)
#router.register(r'scheduling_unit_draft/(?P<scheduling_unit_draft_pk>\d+)/task_draft', viewsets.TaskDraftViewSet) # todo: default view, re-activate or remove the JSON editor one in bottom
router.register(r'task_draft/(?P<task_draft_pk>\d+)/task_blueprint', viewsets.TaskBlueprintViewSet)
router.register(r'task_draft/(?P<task_draft_pk>\d+)/task_relation_draft', viewsets.TaskRelationDraftViewSet)
router.register(r'task_relation_draft/(?P<task_relation_draft_pk>\d+)/task_relation_blueprint', viewsets.TaskRelationBlueprintViewSet)
......@@ -123,9 +123,8 @@ router.register(r'subtask_input_selection_template', viewsets.SubtaskInputSelect
router.register(r'dataproduct_feedback_template', viewsets.DataproductFeedbackTemplateViewSet)
# instances
router.register(r'subtask', viewsets.SubtaskViewSet)
#router.register(r'subtask', viewsets.SubtaskViewSet) # todo: default view, re-activate or remove the JSON editor one in bottom
router.register(r'dataproduct', viewsets.DataproductViewSet)
#router.register(r'dataproduct_relation', viewsets.DataproductRelationViewSet)
router.register(r'subtask_input', viewsets.SubtaskInputViewSet)
router.register(r'subtask_output', viewsets.SubtaskOutputViewSet)
router.register(r'antenna_set', viewsets.AntennaSetViewSet)
......@@ -140,10 +139,10 @@ router.register(r'task_relation_blueprint', viewsets.TaskRelationBlueprintViewSe
# ---
# JSON
router.register(r'task_blueprint_A', viewsets.TaskBlueprintViewSetJSONeditor)
router.register(r'task_blueprint_B', viewsets.TaskBlueprintViewSetReactJSONform)
router.register(r'task_blueprint_C', viewsets.TaskBlueprintViewSetJSONeditorOnline)
#router.register(r'json_editor', viewsets.JSONEditorViewSet)
router.register(r'task_draft', viewsets.TaskDraftViewSetJSONeditorOnline)
router.register(r'scheduling_unit_draft/(?P<scheduling_unit_draft_pk>\d+)/task_draft', viewsets.TaskDraftViewSetJSONeditorOnline)
router.register(r'subtask', viewsets.SubtaskViewSetJSONeditorOnline)
urlpatterns.extend(router.urls)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment