diff --git a/service/api/views.py b/service/api/views.py index e5cda8c..f3e2259 100644 --- a/service/api/views.py +++ b/service/api/views.py @@ -7,14 +7,14 @@ def api_root(request, format=None): return Response({ - 'workflows': reverse('net-list', request=request, format=format), - 'transitions': reverse('transition-list', request=request, format=format), - 'locations': reverse('location-list', request=request, format=format), - 'arcs': reverse('arc-list', request=request, format=format), - 'messages': reverse('message-list', request=request, format=format), + 'workflows': reverse('workflow-list', request=request, format=format), 'cases': reverse('case-list', request=request, format=format), + 'actions': reverse('action-list', request=request, format=format), + 'parameters': reverse('parameter-list', request=request, format=format), + 'values': reverse('value-list', request=request, format=format), + 'conditions': reverse('condition-list', request=request, format=format), + 'messages': reverse('message-list', request=request, format=format), 'users': reverse('user-list', request=request, format=format), - 'tokens': reverse('token-list', request=request, format=format), 'groups': reverse('group-list', request=request, format=format), }) diff --git a/service/workflow/actions.py b/service/workflow/actions.py new file mode 100644 index 0000000..e1c2227 --- /dev/null +++ b/service/workflow/actions.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +""" + +Action Behaviors +================ + +Implementations for the various behaviors action objects may have. + + +""" + +from workflow import models + + +def request_data(case, action): + """Requests a piece of data by creating a message + that is displayed by the submission form.""" + request_message = models.Message( + message_type='Request', + view=action.output.view, + case=case, + response=action.output, + content='The data is requested', + section='upload' + ) + request_message.save() + request_message.origin.add(action), + diff --git a/service/workflow/admin.py b/service/workflow/admin.py index 4e9929e..85aa590 100644 --- a/service/workflow/admin.py +++ b/service/workflow/admin.py @@ -1,54 +1,203 @@ -from django.contrib import admin -from workflow import models +from django.contrib.admin import register +from django.contrib.admin import ModelAdmin +from django.contrib.admin import SimpleListFilter + +from django.core.exceptions import FieldError + from django.contrib.auth.models import User, Group +from workflow.models import Workflow +from workflow.models import Case +from workflow.models import Action +from workflow.models import Parameter +from workflow.models import Condition +from workflow.models import Message +from workflow.models import Value + + +class WorkflowListFilter(SimpleListFilter): + """Filter an object list by workflow""" + + title = 'Workflow' + parameter_name = 'workflow' + default_value = None + + def lookups(self, request, model_admin): + """Returns a list of tuples. The first element in each + tuple is the coded value for the option that will + appear in the URL query. The second element is the + human-readable name for the option that will appear + in the right sidebar.""" + + return sorted([(str(workflow.id), workflow.name) + for workflow in Workflow.objects.all()]) + + def queryset(self, request, queryset): + """Returns the filtered queryset based on the value + provided in the query string and retrievable via + `self.value()`.""" + + if self.value(): + try: + return queryset.filter(workflow_id=self.value()) + except FieldError: + return queryset.filter(workflows=self.value()) + return queryset + + +class CaseListFilter(SimpleListFilter): + """Filter an object list by workflow""" + + title = 'Case' + parameter_name = 'case' + default_value = None + + def lookups(self, request, model_admin): + """Returns a list of tuples. The first element in each + tuple is the coded value for the option that will + appear in the URL query. The second element is the + human-readable name for the option that will appear + in the right sidebar.""" + + return sorted([(str(case.id), case.id) + for case in Case.objects.all()]) -@admin.register(models.Net) -class Net(admin.ModelAdmin): - list_display = ( + def queryset(self, request, queryset): + """Returns the filtered queryset based on the value + provided in the query string and retrievable via + `self.value()`.""" + + if self.value(): + try: + return queryset.filter(case_id=self.value()) + except FieldError: + return queryset.filter(case=self.value()) + return queryset + + +@register(Workflow) +class WorkflowAdmin(ModelAdmin): + + list_display = [ 'name', 'description' - ) + ] -@admin.register(models.Transition) -class Transition(admin.ModelAdmin): - list_display = ( - "name", - "transition_class", - ) -@admin.register(models.Token) -class Token(admin.ModelAdmin): - list_display = ( - 'id', +@register(Case) +class CaseAdmin(ModelAdmin): + + def get_workflow_name(self, object): + return object.workflow.name + + get_workflow_name.admin_order_field = 'name' # Allows column order sorting + get_workflow_name.short_description = 'Workflow Name' # Renames column head + + list_display = [ + "id", + "get_workflow_name" + ] + + list_filter = [ + WorkflowListFilter + ] + + +@register(Action) +class ActionAdmin(ModelAdmin): + + list_display = [ 'name', - 'color', - 'location' - ) + 'description', + 'action_function' + ] + + list_filter = [ + WorkflowListFilter + ] + + +@register(Parameter) +class ParameterAdmin(ModelAdmin): -@admin.register(models.Location) -class Location(admin.ModelAdmin): - list_display = ( + list_display = [ "name", - "description" - ) - -@admin.register(models.Arc) -class Arc(admin.ModelAdmin): - list_display = ( - "type", - "transition", - "location" - ) - -@admin.register(models.Case) -class Case(admin.ModelAdmin): - list_display = ( - "id", - "net", - #"messages" - ) + "view" + ] + + list_filter = [ + WorkflowListFilter + ] + + +@register(Value) +class ValueAdmin(ModelAdmin): + + list_display = [ + 'case', + 'parameter', + 'value', + 'state' + ] + + list_filter = [ + WorkflowListFilter, + CaseListFilter + ] + + +@register(Condition) +class ConditionAdmin(ModelAdmin): + + def get_action_name(self, object): + return object.action.name + + def get_data_name(self, object): + return object.data.name + + def get_workflow_name(self, object): + return object.workflow.name + + get_action_name.admin_order_field = 'name' + get_data_name.admin_order_field = 'name' + get_workflow_name.admin_order_field = 'name' + + get_action_name.short_descripition = 'Action' + get_data_name.short_description = 'Data' + get_workflow_name.short_description = 'Workflow Name' + + list_display = [ + 'get_data_name', + 'state', + 'type', + 'get_action_name', + 'get_workflow_name' + ] + + list_filter = [ + WorkflowListFilter + ] + + +@register(Message) +class MessageAdmin(ModelAdmin): + + def get_case_id(self, object): + return object.case.id + + get_case_id.admin_order_field = 'id' + get_case_id.short_description = 'case' + + list_display = [ + 'id', + 'case', + 'timestamp' + ] + + list_filter = [ + WorkflowListFilter, + CaseListFilter + ] + -@admin.register(models.Message) -class Message(admin.ModelAdmin): - list_display = ("id", ) +# diff --git a/service/workflow/migrations/0001_initial.py b/service/workflow/migrations/0001_initial.py index 23311f6..bb7c2a0 100644 --- a/service/workflow/migrations/0001_initial.py +++ b/service/workflow/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.10.5 on 2017-04-05 15:45 +# Generated by Django 1.11 on 2017-05-01 14:53 from __future__ import unicode_literals import django.contrib.postgres.fields.jsonb @@ -18,25 +18,38 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='Arc', + name='Action', fields=[ ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ('type', models.CharField(choices=[('IN', 'Inhibitory'), ('EX', 'Excitory')], default='EX', max_length=2)), + ('name', models.CharField(max_length=128)), + ('description', models.TextField()), + ('action_function', models.CharField(max_length=128)), + ('group', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='auth.Group')), ], ), migrations.CreateModel( name='Case', fields=[ ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('state', django.contrib.postgres.fields.jsonb.JSONField()), + ('initial_state', django.contrib.postgres.fields.jsonb.JSONField()), ], ), migrations.CreateModel( - name='Location', + name='Condition', fields=[ ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ('name', models.CharField(max_length=128)), - ('description', models.TextField()), - ('type', models.TextField(default='Any')), + ('state', models.CharField(max_length=128)), + ('type', models.CharField(choices=[('E', 'Enable'), ('D', 'Disable')], max_length=1)), + ('action', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workflow.Action')), + ], + ), + migrations.CreateModel( + name='Data', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(blank=True, max_length=128, null=True)), + ('view', models.TextField(blank=True, null=True)), ], ), migrations.CreateModel( @@ -45,12 +58,16 @@ class Migration(migrations.Migration): ('id', models.UUIDField(default=uuid.uuid1, editable=False, primary_key=True, serialize=False)), ('message_type', models.CharField(max_length=24)), ('timestamp', models.DateTimeField(auto_now_add=True)), + ('view', models.TextField(blank=True, null=True)), ('content', models.TextField()), + ('section', models.CharField(default='default', max_length=128)), ('case', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messages', to='workflow.Case')), + ('origin', models.ManyToManyField(default=None, related_name='messages', to='workflow.Action')), + ('response', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='caller', to='workflow.Data')), ], ), migrations.CreateModel( - name='Net', + name='Workflow', fields=[ ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('name', models.CharField(max_length=128)), @@ -58,64 +75,29 @@ class Migration(migrations.Migration): ('group', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='auth.Group')), ], ), - migrations.CreateModel( - name='Token', - fields=[ - ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ('color', django.contrib.postgres.fields.jsonb.JSONField()), - ('name', models.CharField(blank=True, max_length=32, null=True)), - ('case', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tokens', to='workflow.Case')), - ('location', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='parameters', to='workflow.Location')), - ], - ), - migrations.CreateModel( - name='Transition', - fields=[ - ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ('name', models.CharField(max_length=128)), - ('description', models.TextField()), - ('section', models.CharField(default='default', max_length=128)), - ('transition_class', models.CharField(max_length=128)), - ('view', models.TextField(blank=True, null=True)), - ('group', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='auth.Group')), - ('inputs', models.ManyToManyField(blank=True, related_name='targets', through='workflow.Arc', to='workflow.Location')), - ('net', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='transitions', to='workflow.Net')), - ('outputs', models.ManyToManyField(blank=True, related_name='sources', to='workflow.Location')), - ], - ), - migrations.AddField( - model_name='net', - name='starting_tokens', - field=models.ManyToManyField(blank=True, to='workflow.Token'), - ), - migrations.AddField( - model_name='message', - name='origin', - field=models.ManyToManyField(default=None, related_name='messages', to='workflow.Transition'), - ), migrations.AddField( - model_name='message', - name='response', - field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='caller', to='workflow.Transition'), + model_name='condition', + name='data', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='conditions', to='workflow.Data'), ), migrations.AddField( - model_name='location', - name='net', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='locations', to='workflow.Net'), + model_name='condition', + name='workflow', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='workflow.Workflow'), ), migrations.AddField( model_name='case', - name='net', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cases', to='workflow.Net'), + name='data', + field=models.ManyToManyField(blank=True, null=True, related_name='cases', to='workflow.Data'), ), migrations.AddField( - model_name='arc', - name='location', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='arcs', to='workflow.Location'), + model_name='case', + name='workflow', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cases', to='workflow.Workflow'), ), migrations.AddField( - model_name='arc', - name='transition', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='arcs', to='workflow.Transition'), + model_name='action', + name='output', + field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='sources', to='workflow.Data'), ), ] diff --git a/service/workflow/migrations/0002_auto_20170405_1742.py b/service/workflow/migrations/0002_auto_20170501_1920.py similarity index 53% rename from service/workflow/migrations/0002_auto_20170405_1742.py rename to service/workflow/migrations/0002_auto_20170501_1920.py index 09bc2ac..98b888d 100644 --- a/service/workflow/migrations/0002_auto_20170405_1742.py +++ b/service/workflow/migrations/0002_auto_20170501_1920.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.10.5 on 2017-04-05 17:42 +# Generated by Django 1.11 on 2017-05-01 19:20 from __future__ import unicode_literals from django.db import migrations, models @@ -13,14 +13,9 @@ class Migration(migrations.Migration): ] operations = [ - migrations.AddField( - model_name='arc', - name='threshold', - field=models.IntegerField(default=1), - ), migrations.AlterField( - model_name='token', - name='location', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tokens', to='workflow.Location'), + model_name='condition', + name='workflow', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='conditions', to='workflow.Workflow'), ), ] diff --git a/service/workflow/migrations/0003_auto_20170406_1613.py b/service/workflow/migrations/0003_auto_20170406_1613.py deleted file mode 100644 index 4330db3..0000000 --- a/service/workflow/migrations/0003_auto_20170406_1613.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.5 on 2017-04-06 16:13 -from __future__ import unicode_literals - -import django.contrib.postgres.fields.jsonb -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('workflow', '0002_auto_20170405_1742'), - ] - - operations = [ - migrations.AlterField( - model_name='token', - name='color', - field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True), - ), - ] diff --git a/service/workflow/migrations/0003_auto_20170502_1430.py b/service/workflow/migrations/0003_auto_20170502_1430.py new file mode 100644 index 0000000..37c3713 --- /dev/null +++ b/service/workflow/migrations/0003_auto_20170502_1430.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11 on 2017-05-02 14:30 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('workflow', '0002_auto_20170501_1920'), + ] + + operations = [ + migrations.RenameModel( + old_name='Data', + new_name='Parameter', + ), + ] diff --git a/service/workflow/migrations/0004_auto_20170406_1839.py b/service/workflow/migrations/0004_auto_20170406_1839.py deleted file mode 100644 index 22e6119..0000000 --- a/service/workflow/migrations/0004_auto_20170406_1839.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.5 on 2017-04-06 18:39 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('workflow', '0003_auto_20170406_1613'), - ] - - operations = [ - migrations.AlterField( - model_name='token', - name='case', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='tokens', to='workflow.Case'), - ), - ] diff --git a/service/workflow/migrations/0004_auto_20170502_1451.py b/service/workflow/migrations/0004_auto_20170502_1451.py new file mode 100644 index 0000000..b181dc3 --- /dev/null +++ b/service/workflow/migrations/0004_auto_20170502_1451.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11 on 2017-05-02 14:51 +from __future__ import unicode_literals + +import django.contrib.postgres.fields.jsonb +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('workflow', '0003_auto_20170502_1430'), + ] + + operations = [ + migrations.RenameField( + model_name='case', + old_name='data', + new_name='parameter', + ), + migrations.AlterField( + model_name='case', + name='initial_state', + field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default={}), + ), + migrations.AlterField( + model_name='case', + name='state', + field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default={}), + ), + ] diff --git a/service/workflow/migrations/0005_arc_net.py b/service/workflow/migrations/0005_arc_net.py deleted file mode 100644 index ab0a7ac..0000000 --- a/service/workflow/migrations/0005_arc_net.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-11 19:56 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('workflow', '0004_auto_20170406_1839'), - ] - - operations = [ - migrations.AddField( - model_name='arc', - name='net', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='arcs', to='workflow.Net'), - ), - ] diff --git a/service/workflow/migrations/0005_auto_20170502_1604.py b/service/workflow/migrations/0005_auto_20170502_1604.py new file mode 100644 index 0000000..bf75652 --- /dev/null +++ b/service/workflow/migrations/0005_auto_20170502_1604.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11 on 2017-05-02 16:04 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('workflow', '0004_auto_20170502_1451'), + ] + + operations = [ + migrations.RemoveField( + model_name='case', + name='parameter', + ), + migrations.AddField( + model_name='case', + name='parameters', + field=models.ManyToManyField(blank=True, related_name='cases', to='workflow.Parameter'), + ), + ] diff --git a/service/workflow/migrations/0008_auto_20170412_1508.py b/service/workflow/migrations/0006_auto_20170502_1618.py similarity index 68% rename from service/workflow/migrations/0008_auto_20170412_1508.py rename to service/workflow/migrations/0006_auto_20170502_1618.py index 922ef80..5dcedcc 100644 --- a/service/workflow/migrations/0008_auto_20170412_1508.py +++ b/service/workflow/migrations/0006_auto_20170502_1618.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-12 15:08 +# Generated by Django 1.11 on 2017-05-02 16:18 from __future__ import unicode_literals from django.db import migrations, models @@ -9,13 +9,13 @@ class Migration(migrations.Migration): dependencies = [ - ('workflow', '0007_auto_20170412_1451'), + ('workflow', '0005_auto_20170502_1604'), ] operations = [ migrations.AlterField( model_name='message', name='response', - field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='caller', to='workflow.Location'), + field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='messages', to='workflow.Parameter'), ), ] diff --git a/service/workflow/migrations/0006_transition_static_args.py b/service/workflow/migrations/0006_transition_static_args.py deleted file mode 100644 index 1db1407..0000000 --- a/service/workflow/migrations/0006_transition_static_args.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-12 14:18 -from __future__ import unicode_literals - -import django.contrib.postgres.fields.jsonb -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('workflow', '0005_arc_net'), - ] - - operations = [ - migrations.AddField( - model_name='transition', - name='static_args', - field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True), - ), - ] diff --git a/service/workflow/migrations/0007_auto_20170412_1451.py b/service/workflow/migrations/0007_auto_20170412_1451.py deleted file mode 100644 index 1d199e4..0000000 --- a/service/workflow/migrations/0007_auto_20170412_1451.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-12 14:51 -from __future__ import unicode_literals - -import django.contrib.postgres.fields.jsonb -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('workflow', '0006_transition_static_args'), - ] - - operations = [ - migrations.AlterField( - model_name='transition', - name='static_args', - field=django.contrib.postgres.fields.jsonb.JSONField(default={}), - ), - ] diff --git a/service/workflow/migrations/0007_workflow_actions.py b/service/workflow/migrations/0007_workflow_actions.py new file mode 100644 index 0000000..d1154e2 --- /dev/null +++ b/service/workflow/migrations/0007_workflow_actions.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11 on 2017-05-03 20:04 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('workflow', '0006_auto_20170502_1618'), + ] + + operations = [ + migrations.AddField( + model_name='workflow', + name='actions', + field=models.ManyToManyField(related_name='workflows', to='workflow.Action'), + ), + ] diff --git a/service/workflow/migrations/0008_auto_20170504_1321.py b/service/workflow/migrations/0008_auto_20170504_1321.py new file mode 100644 index 0000000..3297b95 --- /dev/null +++ b/service/workflow/migrations/0008_auto_20170504_1321.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11 on 2017-05-04 13:21 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('workflow', '0007_workflow_actions'), + ] + + operations = [ + migrations.AlterField( + model_name='condition', + name='action', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='action', to='workflow.Action'), + ), + migrations.AlterField( + model_name='workflow', + name='actions', + field=models.ManyToManyField(blank=True, null=True, related_name='workflows', to='workflow.Action'), + ), + ] diff --git a/service/workflow/migrations/0009_auto_20170412_1539.py b/service/workflow/migrations/0009_auto_20170412_1539.py deleted file mode 100644 index a3711d3..0000000 --- a/service/workflow/migrations/0009_auto_20170412_1539.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-12 15:39 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('workflow', '0008_auto_20170412_1508'), - ] - - operations = [ - migrations.RemoveField( - model_name='transition', - name='view', - ), - migrations.AddField( - model_name='message', - name='view', - field=models.TextField(blank=True, null=True), - ), - ] diff --git a/service/workflow/migrations/0009_auto_20170504_1325.py b/service/workflow/migrations/0009_auto_20170504_1325.py new file mode 100644 index 0000000..f454ae1 --- /dev/null +++ b/service/workflow/migrations/0009_auto_20170504_1325.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11 on 2017-05-04 13:25 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('workflow', '0008_auto_20170504_1321'), + ] + + operations = [ + migrations.AlterField( + model_name='condition', + name='action', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='conditions', to='workflow.Action'), + ), + ] diff --git a/service/workflow/migrations/0010_auto_20170504_1352.py b/service/workflow/migrations/0010_auto_20170504_1352.py new file mode 100644 index 0000000..ca4fa07 --- /dev/null +++ b/service/workflow/migrations/0010_auto_20170504_1352.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11 on 2017-05-04 13:52 +from __future__ import unicode_literals + +import django.contrib.postgres.fields.jsonb +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('workflow', '0009_auto_20170504_1325'), + ] + + operations = [ + migrations.AddField( + model_name='case', + name='content', + field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default={}), + ), + migrations.AlterField( + model_name='condition', + name='state', + field=models.CharField(blank=True, max_length=128, null=True), + ), + ] diff --git a/service/workflow/migrations/0010_location_view.py b/service/workflow/migrations/0010_location_view.py deleted file mode 100644 index f37c815..0000000 --- a/service/workflow/migrations/0010_location_view.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-12 15:40 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('workflow', '0009_auto_20170412_1539'), - ] - - operations = [ - migrations.AddField( - model_name='location', - name='view', - field=models.TextField(blank=True, null=True), - ), - ] diff --git a/service/workflow/migrations/0011_auto_20170412_1638.py b/service/workflow/migrations/0011_auto_20170412_1638.py deleted file mode 100644 index 65d1b0c..0000000 --- a/service/workflow/migrations/0011_auto_20170412_1638.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-12 16:38 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('workflow', '0010_location_view'), - ] - - operations = [ - migrations.RemoveField( - model_name='transition', - name='section', - ), - migrations.AddField( - model_name='message', - name='section', - field=models.CharField(default='default', max_length=128), - ), - ] diff --git a/service/workflow/migrations/0011_value.py b/service/workflow/migrations/0011_value.py new file mode 100644 index 0000000..94ff945 --- /dev/null +++ b/service/workflow/migrations/0011_value.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11 on 2017-05-08 17:47 +from __future__ import unicode_literals + +import django.contrib.postgres.fields.jsonb +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('workflow', '0010_auto_20170504_1352'), + ] + + operations = [ + migrations.CreateModel( + name='Value', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('value', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), + ('state', models.CharField(blank=True, max_length=128, null=True)), + ('case', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='values', to='workflow.Case')), + ('parameter', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='values', to='workflow.Parameter')), + ], + ), + ] diff --git a/service/workflow/migrations/0012_auto_20170413_1456.py b/service/workflow/migrations/0012_workflow_parameters.py similarity index 55% rename from service/workflow/migrations/0012_auto_20170413_1456.py rename to service/workflow/migrations/0012_workflow_parameters.py index ec30b47..a861836 100644 --- a/service/workflow/migrations/0012_auto_20170413_1456.py +++ b/service/workflow/migrations/0012_workflow_parameters.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-13 14:56 +# Generated by Django 1.11 on 2017-05-08 17:58 from __future__ import unicode_literals from django.db import migrations, models @@ -8,13 +8,13 @@ class Migration(migrations.Migration): dependencies = [ - ('workflow', '0011_auto_20170412_1638'), + ('workflow', '0011_value'), ] operations = [ - migrations.AlterField( - model_name='net', - name='starting_tokens', - field=models.ManyToManyField(blank=True, related_name='net', to='workflow.Token'), + migrations.AddField( + model_name='workflow', + name='parameters', + field=models.ManyToManyField(blank=True, related_name='workflows', to='workflow.Parameter'), ), ] diff --git a/service/workflow/migrations/0013_token_request_message.py b/service/workflow/migrations/0013_token_request_message.py deleted file mode 100644 index 786d6e8..0000000 --- a/service/workflow/migrations/0013_token_request_message.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-13 17:20 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('workflow', '0012_auto_20170413_1456'), - ] - - operations = [ - migrations.AddField( - model_name='token', - name='request_message', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='message', to='workflow.Message'), - ), - ] diff --git a/service/workflow/migrations/0014_auto_20170413_1727.py b/service/workflow/migrations/0014_auto_20170413_1727.py deleted file mode 100644 index 4812916..0000000 --- a/service/workflow/migrations/0014_auto_20170413_1727.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-13 17:27 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('workflow', '0013_token_request_message'), - ] - - operations = [ - migrations.AlterField( - model_name='token', - name='request_message', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='response_token', to='workflow.Message'), - ), - ] diff --git a/service/workflow/migrations/0015_auto_20170417_1744.py b/service/workflow/migrations/0015_auto_20170417_1744.py deleted file mode 100644 index 55da4a6..0000000 --- a/service/workflow/migrations/0015_auto_20170417_1744.py +++ /dev/null @@ -1,30 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-17 17:44 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('workflow', '0014_auto_20170413_1727'), - ] - - operations = [ - migrations.RemoveField( - model_name='net', - name='starting_tokens', - ), - migrations.AddField( - model_name='token', - name='net', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='tokens', to='workflow.Net'), - ), - migrations.AddField( - model_name='token', - name='starts', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='starting_tokens', to='workflow.Net'), - ), - ] diff --git a/service/workflow/migrations/0016_auto_20170420_2133.py b/service/workflow/migrations/0016_auto_20170420_2133.py deleted file mode 100644 index 3362cfc..0000000 --- a/service/workflow/migrations/0016_auto_20170420_2133.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-20 21:33 -from __future__ import unicode_literals - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('workflow', '0015_auto_20170417_1744'), - ] - - operations = [ - migrations.RenameField( - model_name='transition', - old_name='static_args', - new_name='permanent_tokens', - ), - ] diff --git a/service/workflow/migrations/0017_auto_20170421_1534.py b/service/workflow/migrations/0017_auto_20170421_1534.py deleted file mode 100644 index 43b7402..0000000 --- a/service/workflow/migrations/0017_auto_20170421_1534.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-21 15:34 -from __future__ import unicode_literals - -import django.contrib.postgres.fields.jsonb -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('workflow', '0016_auto_20170420_2133'), - ] - - operations = [ - migrations.AlterField( - model_name='transition', - name='permanent_tokens', - field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default={}, null=True), - ), - ] diff --git a/service/workflow/migrations/0018_auto_20170421_1535.py b/service/workflow/migrations/0018_auto_20170421_1535.py deleted file mode 100644 index 538282d..0000000 --- a/service/workflow/migrations/0018_auto_20170421_1535.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-21 15:35 -from __future__ import unicode_literals - -import django.contrib.postgres.fields.jsonb -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('workflow', '0017_auto_20170421_1534'), - ] - - operations = [ - migrations.AlterField( - model_name='transition', - name='permanent_tokens', - field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=[], null=True), - ), - ] diff --git a/service/workflow/migrations/0019_token_unique_at_location.py b/service/workflow/migrations/0019_token_unique_at_location.py deleted file mode 100644 index 5d79300..0000000 --- a/service/workflow/migrations/0019_token_unique_at_location.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-24 15:14 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('workflow', '0018_auto_20170421_1535'), - ] - - operations = [ - migrations.AddField( - model_name='token', - name='unique_at_location', - field=models.BooleanField(default=False), - ), - ] diff --git a/service/workflow/migrations/0020_message_response_token_name.py b/service/workflow/migrations/0020_message_response_token_name.py deleted file mode 100644 index 6fd347d..0000000 --- a/service/workflow/migrations/0020_message_response_token_name.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-24 17:25 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('workflow', '0019_token_unique_at_location'), - ] - - operations = [ - migrations.AddField( - model_name='message', - name='response_token_name', - field=models.CharField(blank=True, default=None, max_length=128, null=True), - ), - ] diff --git a/service/workflow/migrations/0021_auto_20170425_1616.py b/service/workflow/migrations/0021_auto_20170425_1616.py deleted file mode 100644 index 840d7ca..0000000 --- a/service/workflow/migrations/0021_auto_20170425_1616.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11 on 2017-04-25 16:16 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('workflow', '0020_message_response_token_name'), - ] - - operations = [ - migrations.RemoveField( - model_name='token', - name='request_message', - ), - migrations.AddField( - model_name='token', - name='request_messages', - field=models.ManyToManyField(blank=True, null=True, related_name='response_tokens', to='workflow.Message'), - ), - ] diff --git a/service/workflow/models.py b/service/workflow/models.py index 9fd2a8a..0fb3ee9 100644 --- a/service/workflow/models.py +++ b/service/workflow/models.py @@ -1,280 +1,294 @@ # -*- coding: utf-8 -*- -'''Workflow Models''' +"""Workflow Models""" from itertools import chain +from uuid import uuid1 +from uuid import uuid4 -from django.db import models -from django.contrib.postgres.fields import JSONField -from django.contrib.auth.models import User, Group from django.db import transaction -import uuid +from django.db.models import CharField +from django.db.models import DateTimeField +from django.db.models import ForeignKey +from django.db.models import ManyToManyField +from django.db.models import Model +from django.db.models import TextField +from django.db.models import UUIDField +from django.contrib.auth.models import Group +from django.contrib.auth.models import User +from django.contrib.postgres.fields import JSONField +from workflow import actions from workflow.utils import camelCase -#from workflow import managers -from workflow import transitions - -import ipdb; - -#ipdb.set_trace() - -class Net(models.Model): - - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - name = models.CharField(max_length=128) - description = models.TextField() - group = models.ForeignKey('auth.Group', default=1) - - def get_fireable_transitions(self): - transitions = [transition for transition in self.transitions.all() if transition.is_fireable()] - print(transitions) - return transitions - - def get_enabled_transitions(self): - return [transition for transition in self.transitions.all() if transition.is_enabled()] - - def fire_all(self): - pass - - def fire(self): - fireable_transitions = self.get_fireable_transitions() - consumed_tokens = Token.objects.none() - - def validate_net(self): - print("Validating Net...") - for transition in self.transitions.all(): - if not transition.inputs.exists(): - import ipdb; ipdb.set_trace() - raise Exception - - def wake(self): - - print('\n###########################################################################') - print('############################## WAKING NET #################################') - print('###########################################################################\n') - self.validate_net() - - while True: - - fireable_transitions = self.get_fireable_transitions() - if not fireable_transitions: - print('\n###########################################################################') - print('############################## FIRING STALLED #############################') - print('###########################################################################\n') - break - - # All enabled transitions must fire during each - # round to prevent corruption of net state. - with transaction.atomic(): - - sinks = [] - stale_tokens = [] - - # Fire the transitions - for transition in fireable_transitions: - print(f'\n****************************************************************') - print(f'Evaluating at transition {transition}...') - print(f'################################################################\n') - for case in transition.fireable_cases(): - print(f'Evaluating case {case}') - tokens = [] - for token in transition.all_tokens(): - if token.case is not None: - if token.case.id == case.id: - print(token) - print(token.__dict__) - tokens.append(token) - print(tokens) - try: - sink = transition.fire([token.__dict__ for token in tokens], case) - except Exception as e: - print("transition did not fire") - raise e - - print(f'\nNew Tokens: {sink}') - # Mark tokens as stale as transitions that depend on them fire. - # Defer deletion until after this round of transitions have fired. - stale_tokens = list(chain(stale_tokens, tokens)) - # Defer creation until after this round of transitions have fired. - #import ipdb; ipdb.set_trace() - sinks = list(chain(sinks, sink)) - - # All transitions have fired, delete stale tokens - print(f'\nDeleting stale tokens') - for token in stale_tokens: - print(f'Token {token}') - token.delete() - - # All transitions have fired, now create tokens - print('\nSaving newly created tokens...') - for sink in sinks: - print(f'Token {sink}') - Token(**sink).save() - - print('') - def __str__(self): - return self.name +def print_section_header(string): + print('\n\n\033[38;2;230;223;196m\033[1m################################################################################') + print('') + print(f'{string}') + print('################################################################################\033[0m\n\n') + + +def print_subheading_with_context(subheading, context): + print('\n\n\033[38;2;230;223;196m\033[1m--------------------------------------------------------------------------------') + print(f'\n{subheading}: \033[0m{context}\033[0m\n') + + +class ConditionException(Exception): + pass +class Workflow(Model): + """Workflow Model""" -class Transition(models.Model): - - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - name = models.CharField(max_length=128) - description = models.TextField() - group = models.ForeignKey('auth.Group', default=1) - transition_class = models.CharField(max_length=128) - inputs = models.ManyToManyField('Location', related_name='targets', through='Arc', blank=True) - outputs = models.ManyToManyField('Location', related_name='sources', blank=True) - net = models.ForeignKey('Net', null=True, blank=True, related_name='transitions') - permanent_tokens = JSONField(null=True, blank=True, default=[]) - - def fire(self, tokens, case): - print(self.name) - print(self.description) - print(f'\nInputs:') - for input in self.inputs.all(): - print(input) - print('\nOutputs') - for output in self.outputs.all(): - print(output) - tokens.extend(self.permanent_tokens) - print('\nTokens') - for token in tokens: - print(token) - transition_fn = getattr(transitions, self.transition_class) - print(f'\nTransition: {self.transition_class}') - print("\n* ___ FIRING ___ *\n") - sink = transition_fn(**{ - "tokens": tokens, - "case": case, - "transition": self, - }) - return sink - - def all_tokens(self): - tokens = [] - for arc in self.arcs.all(): - tokens = list(chain(tokens, arc.location.tokens.all())) - print(tokens) - return tokens - - def fireable_cases(self): - cases = [] - print('finding fireable cases') - for case in self.net.cases.all(): - print(f'Checking case: {case}') - fires = True - for arc in self.arcs.all(): - print(f"checking arc: {arc}") - print(arc.type) - print(arc.threshold) - tokens = arc.location.tokens.filter(case__id=case.id) - print(tokens) - print(tokens.count()) - if arc.type == 'IN': - if tokens.count() < arc.threshold: - print(f'{case} is fireable') - continue - if arc.type == 'EX': - if tokens.count() >= arc.threshold: - print(f'{case} is fireable') - continue - fires = False - break - if fires: - cases.append(case) - - return cases - - def enabled_cases(self): - querysets = [arc.location.tokens.all() for arc in self.arcs.all() if arc.location.type != 'IO'] - return [case for case in self.net.cases.all() if - all([queryset.filter(case__id=case.id).exists() for queryset in querysets])] - - def is_fireable(self): - return any(self.fireable_cases()) - - def is_enabled(self): - return any(self.enabled_cases()) + id = UUIDField( + primary_key=True, + default=uuid4, + editable=False + ) + + name = CharField(max_length=128) + description = TextField() + + group = ForeignKey( + 'auth.Group', + default=1 + ) + + parameters = ManyToManyField( + 'Parameter', + related_name='workflows', + blank=True + ) + + actions = ManyToManyField( + 'Action', + related_name='workflows', + null=True, + blank=True + ) + + def update(self, case): + print_section_header('Checking Actions') + for action in self.actions.all(): + print_subheading_with_context('Action', action.name) + try: + if action.conditions.exists(): + for condition in action.conditions.all(): + print(f'\n\033[38;2;230;223;196mChecking condition: {condition}\033[0m') + print(f'{condition.state} ? {case.state.get(condition.id)}', end='') + parameter = condition.data + try: + value = parameter.values.get(case=case) + except: + value = Value() + value.parameter = parameter + value.state + if condition.state == value.state: + print(' ... \033[38;2;0;255;0m\033[1mOk\033[0m') + continue + print(' ... \033[38;2;255;0;0m\033[1mFailed\033[0m\n') + raise ConditionException + print('\n\033[38;2;0;255;0m\033[1m===== ACTION FIRED\n\033[0m') + getattr(actions, action.action_function)(case, action) + else: + print('\n\033[94mNo conditions to check.\033[0m\n') + except ConditionException: + pass + print_section_header('Finished Checking Actions') def __str__(self): return self.name -class Token(models.Model): +class Case(Model): + """Case Model""" - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - color = JSONField(null=True, blank=True) - case = models.ForeignKey('Case', related_name='tokens', null=True, blank=True) - location = models.ForeignKey('Location', related_name='tokens') - name = models.CharField(max_length=32, null=True, blank=True) - request_messages = models.ManyToManyField('Message', null=True, blank=True, related_name='response_tokens') - starts = models.ForeignKey('Net', related_name='starting_tokens', null=True, blank=True) - net = models.ForeignKey('Net', related_name='tokens', null=True, blank=True) - unique_at_location = models.BooleanField(default=False) + id = UUIDField( + primary_key=True, + default=uuid4, + editable=False + ) - def save(self, *args, **kwargs): - for token in Token.objects.filter(location=self.location).filter(name=self.name).filter(case=self.case): - print(f'Checking Token: {token}') - if token.unique_at_location == True: - print(f'Deleting Token: {token}') - token.delete() - super().save(*args, **kwargs) + parameters = ManyToManyField( + 'Parameter', + related_name='cases', + blank=True + ) + + content = JSONField(blank=True, default={}) + state = JSONField(blank=True, default={}) + + workflow = ForeignKey( + 'Workflow', + related_name='cases', + blank=True, + null=True + ) + + initial_state = JSONField(blank=True, default={}) def __str__(self): - return '{} @ {}'.format(self.name, self.location.name) + return str(self.id.urn) -class Location(models.Model): +class Action(Model): + """Action Model""" - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - name = models.CharField(max_length=128) - description = models.TextField() - type = models.TextField(default='Any') - view = models.TextField(null=True, blank=True) - net = models.ForeignKey('Net', null=True, blank=True, related_name='locations') + id = UUIDField(primary_key=True, default=uuid4, editable=False) + name = CharField(max_length=128) + description = TextField() + group = ForeignKey('auth.Group', default=1) + action_function = CharField(max_length=128) + output = ForeignKey('Parameter', related_name='sources', blank=True) def __str__(self): return self.name -class Arc(models.Model): +class Parameter(Model): + """Parameter Model""" + + id = UUIDField( + primary_key=True, + default=uuid4, + editable=False + ) + + name = CharField( + max_length=128, + null=True, + blank=True + ) - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - location = models.ForeignKey('Location', related_name='arcs') - transition = models.ForeignKey('Transition', related_name='arcs') - net = models.ForeignKey('Net', related_name='arcs', null=True, blank=True) - type = models.CharField(max_length=2, choices=(('IN', 'Inhibitory'), ('EX', 'Excitory')), default='EX') - threshold = models.IntegerField(default=1) + view = TextField( + null=True, + blank=True + ) + + def save(self, *args, **kwargs): + super().save(*args, **kwargs) def __str__(self): - return f'{self.location.name} => {self.transition.name}' + return self.name -class Case(models.Model): +class Value(Model): + """Value Model""" - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - net = models.ForeignKey('Net', related_name='cases', blank=True, null=True) + id = UUIDField( + primary_key=True, + default=uuid4, + editable=False + ) - def __str__(self): - return str(self.id.urn) + value = JSONField( + null=True, + blank=True + ) + + state = CharField( + max_length=128, + null=True, + blank=True + ) + parameter = ForeignKey( + 'Parameter', + related_name='values' + ) -class Message(models.Model): + case = ForeignKey( + 'Case', + related_name='values' + ) - id = models.UUIDField(primary_key=True, default=uuid.uuid1, editable=False) - message_type = models.CharField(max_length=24) - timestamp = models.DateTimeField(auto_now_add=True) - view = models.TextField(null=True, blank=True) - case = models.ForeignKey('Case', related_name='messages') - origin = models.ManyToManyField('Transition', related_name='messages', default=None) - response = models.ForeignKey('Location', related_name='caller', default=None, null=True) - response_token_name = models.CharField(max_length=128, default=None, blank=True, null=True) - content = models.TextField() - section = models.CharField(max_length=128, default='default') + def save(self, *args, **kwargs): + super().save(*args, **kwargs) + self.case.workflow.update(self.case) + + +class Condition(Model): + """Condition Model""" + + id = UUIDField( + primary_key=True, + default=uuid4, + editable=False + ) + + data = ForeignKey( + 'Parameter', + related_name='conditions' + ) + + state = CharField( + max_length=128, + null=True, + blank=True + ) + + type = CharField( + max_length=1, + choices=( + ('E', 'Enable'), + ('D', 'Disable') + ) + ) + + action = ForeignKey( + 'Action', + related_name='conditions' + ) + + workflow = ForeignKey( + 'Workflow', + related_name='conditions' + ) + + def __str__(self): + return f'{self.action}: {self.type} {self.data} {self.state}' + + +class Message(Model): + """Message Model""" + + id = UUIDField( + primary_key=True, + default=uuid1, + editable=False + ) + + case = ForeignKey( + 'Case', + related_name='messages' + ) + + origin = ManyToManyField( + 'Action', + related_name='messages', + default=None + ) + + section = CharField( + max_length=128, + default='default' + ) + + response = ForeignKey( + 'Parameter', + related_name='messages', + default=None, + null=True + ) + + message_type = CharField(max_length=24) + view = TextField(null=True, blank=True) + content = TextField() + timestamp = DateTimeField(auto_now_add=True) def __str__(self): return 'Message: {}'.format(self.id.urn) + + +# diff --git a/service/workflow/routers.py b/service/workflow/routers.py index cf12f38..9fbae17 100644 --- a/service/workflow/routers.py +++ b/service/workflow/routers.py @@ -6,19 +6,27 @@ from rest_framework.urlpatterns import format_suffix_patterns from rest_framework_nested import routers -from workflow import views +from workflow.views import WorkflowViewSet +from workflow.views import CaseViewSet +from workflow.views import ActionViewSet +from workflow.views import ParameterViewSet +from workflow.views import ConditionViewSet +from workflow.views import ValueViewSet +from workflow.views import MessageViewSet +from workflow.views import GroupViewSet +from workflow.views import UserViewSet router = routers.SimpleRouter(trailing_slash=False) -router.register(r'nets', views.Net) -router.register(r'transitions', views.Transition) -router.register(r'locations', views.Location) -router.register(r'messages', views.Message) -router.register(r'cases', views.Case) -router.register(r'users', views.User) -router.register(r'groups', views.Group) -router.register(r'tokens', views.Token) -router.register(r'arcs', views.Arc) +router.register(r'workflows', WorkflowViewSet) +router.register(r'cases', CaseViewSet) +router.register(r'actions', ActionViewSet) +router.register(r'parameters', ParameterViewSet) +router.register(r'conditions', ConditionViewSet) +router.register(r'messages', MessageViewSet) +router.register(r'values', ValueViewSet) +router.register(r'groups', GroupViewSet) +router.register(r'users', UserViewSet) #transition_router = routers.NestedSimpleRouter(router, r'transitions', lookup='transition') diff --git a/service/workflow/serializers.py b/service/workflow/serializers.py index b01619d..a7eded0 100644 --- a/service/workflow/serializers.py +++ b/service/workflow/serializers.py @@ -2,194 +2,271 @@ """Workflow Serializers""" +from django.contrib.auth.models import User +from django.contrib.auth.models import Group + from rest_framework.serializers import CharField -from rest_framework_json_api import serializers, relations -from django.contrib.auth.models import User, Group +from rest_framework.serializers import JSONField +from rest_framework_json_api.serializers import ModelSerializer +from rest_framework_json_api.relations import ResourceRelatedField -from workflow import models +from workflow.models import Workflow +from workflow.models import Case +from workflow.models import Action +from workflow.models import Parameter +from workflow.models import Condition +from workflow.models import Message +from workflow.models import Value -class Net(serializers.ModelSerializer): +class WorkflowSerializer(ModelSerializer): + """Workflow Serializer""" + + name = CharField(max_length=128) + actions = ResourceRelatedField( + queryset=Action.objects.all(), + many=True, + required=False + ) class Meta: - resource_name = 'nets' - model = models.Net - fields = ( + resource_name = 'workflows' + model = Workflow + fields = [ 'id', 'name', 'description', 'group', 'cases', - 'transitions', - 'locations', - ) + 'conditions', + 'actions' + ] -class Transition(serializers.ModelSerializer): +class CaseSerializer(ModelSerializer): + """Case Serializer""" - name = CharField(max_length=128, required=False) - transition_class = CharField(max_length=128, required=False) - group = relations.ResourceRelatedField( - queryset=Group.objects.all() + parameters = ResourceRelatedField( + queryset=Parameter.objects.all(), + many=True, + required=False + ) + + workflow = ResourceRelatedField( + queryset=Workflow.objects.all(), + required=True ) - inputs = relations.ResourceRelatedField( + + messages = ResourceRelatedField( + queryset=Message.objects.all(), many=True, - queryset=models.Location.objects.all() + required=False ) - outputs = relations.ResourceRelatedField( + + values = ResourceRelatedField( + queryset=Value.objects.all(), many=True, - queryset=models.Location.objects.all() + required=False + ) + + initial_state = JSONField( + required=False ) class Meta: - resource_name = 'transitions' - model = models.Transition - fields = ( + resource_name = 'cases' + model = Case + fields = [ 'id', - 'name', - 'description', - 'group', - 'transition_class', - 'inputs', - 'outputs', - 'messages', - 'net', - 'arcs' - ) + 'parameters', + 'values', + 'workflow', + 'initial_state', + 'messages' + ] + +class ActionSerializer(ModelSerializer): + """Action Serializer""" -class Token(serializers.ModelSerializer): + name = CharField(max_length=128) - location = relations.ResourceRelatedField( - required=False, - queryset=models.Location.objects.all() + group = ResourceRelatedField( + queryset=Group.objects.all() ) - net = relations.ResourceRelatedField( - required=False, - queryset=models.Net.objects.all() + ouput = ResourceRelatedField( + queryset=Parameter.objects.all() ) - case = relations.ResourceRelatedField( - required=False, - queryset=models.Case.objects.all() + conditions = ResourceRelatedField( + queryset=Condition.objects.all() ) - request_messages = relations.ResourceRelatedField( - required=False, - many=True, - queryset=models.Message.objects.all() + messages = ResourceRelatedField( + queryset=Message.objects.all() ) class Meta: - resource_name = 'tokens' - model = models.Token - fields = ( - 'color', - 'case', - 'location', - 'request_messages', + resource_name = 'cases' + model = Action + fields = [ + 'id', 'name', - 'net' - ) + 'description', + 'group', + 'action_function', + 'output', + 'conditions', + 'messages' + ] + -class Location(serializers.ModelSerializer): +class ParameterSerializer(ModelSerializer): + """Parameter Serializer""" - sources = relations.ResourceRelatedField( + cases = ResourceRelatedField( many=True, - queryset=models.Transition.objects.all() + queryset=Case.objects.all() ) - targets = relations.ResourceRelatedField( + + sources = ResourceRelatedField( many=True, - queryset=models.Transition.objects.all() + queryset=Action.objects.all() + ) + + conditions = ResourceRelatedField( + many=True, + queryset=Condition.objects.all() + ) + + values = ResourceRelatedField( + many=True, + queryset=Value.objects.all() + ) + + workflows = ResourceRelatedField( + many=True, + queryset=Workflow.objects.all() ) class Meta: - resource_name = 'locations' - model = models.Location - fields = ( + resource_name = 'parameters' + model = Parameter + fields = [ + 'id', 'name', - 'description', - 'type', - 'net', - 'targets', - 'arcs', - 'tokens', - 'sources' - ) + 'view', + 'cases', + 'values', + 'messages', + 'sources', + 'workflows', + 'conditions' + ] -class Arc(serializers.ModelSerializer): +class ValueSerializer(ModelSerializer): + """Value Serializer""" - class Meta: - resource_name = 'arcs' - model = models.Arc - fields = ( - 'type', - ) + value = JSONField( + required=False + ) + state = CharField( + max_length=128 + ) -class Case(serializers.ModelSerializer): + case = ResourceRelatedField( + queryset=Case.objects.all() + ) - messages = relations.ResourceRelatedField( - required=False, - many=True, - queryset=models.Message.objects.all() + parameter = ResourceRelatedField( + queryset=Parameter.objects.all(), ) - class Meta: - resource_name = 'cases' - model = models.Case - fields = ( + class Meta(): + resource_name = 'values' + model = Value + fields = [ 'id', - 'net', - 'messages' - ) + 'value', + 'state', + 'case', + 'parameter' + ] -class Message(serializers.ModelSerializer): - - response_tokens = relations.ResourceRelatedField( - required=False, - many=True, - queryset=models.Token.objects.all() +class ConditionSerializer(ModelSerializer): + """Condition Serializer""" + + parameter = ResourceRelatedField( + queryset=Parameter.objects.all() + ) + + action = ResourceRelatedField( + queryset=Action.objects.all() + ) + + workflow = ResourceRelatedField( + queryset=Workflow.objects.all() ) + class Meta: + resource_name = 'condition' + model = Condition + fields = [ + 'id', + 'parameter', + 'data', + 'state', + 'type', + 'action', + 'workflow' + ] + + +class MessageSerializer(ModelSerializer): + """Message Serializer""" + class Meta: resource_name = 'messages' - model = models.Message - fields = ( + model = Message + fields = [ 'id', 'message_type', 'timestamp', + 'view', + 'case', 'origin', 'response', - 'case', 'content', - 'view', - 'section', - 'response_tokens', - 'response_token_name' - ) + 'section' + ] -class User(serializers.ModelSerializer): +class UserSerializer(ModelSerializer): + """User Serializer""" class Meta: resource_name = 'users' model = User - fields = ( + fields = [ 'id', - 'username', - ) + 'username' + ] -class Group(serializers.ModelSerializer): +class GroupSerializer(ModelSerializer): + """Group Serializer""" class Meta: resource_name = 'groups' model = Group - fields = ( - 'name', - ) + fields = [ + 'name' + ] + + +# diff --git a/service/workflow/views.py b/service/workflow/views.py index 91bcb34..e3e14bb 100644 --- a/service/workflow/views.py +++ b/service/workflow/views.py @@ -2,6 +2,8 @@ """Workflow Views""" +import json +import copy from django.core.exceptions import PermissionDenied from django.contrib.auth.models import Group @@ -11,179 +13,93 @@ from rest_framework import viewsets from rest_framework import status from rest_framework.decorators import detail_route +from rest_framework.exceptions import APIException from rest_framework.response import Response +from rest_framework.viewsets import ModelViewSet from rest_framework_json_api.views import RelationshipView -from rest_framework.exceptions import APIException - -import json -import copy - -from workflow import models -from workflow import serializers -from workflow import operations -from workflow import utils +from workflow.models import Workflow +from workflow.models import Case +from workflow.models import Action +from workflow.models import Parameter +from workflow.models import Condition +from workflow.models import Message +from workflow.models import Value -class Net(viewsets.ModelViewSet): - queryset = models.Net.objects.all() - serializer_class = serializers.Net +from workflow.serializers import WorkflowSerializer +from workflow.serializers import CaseSerializer +from workflow.serializers import ActionSerializer +from workflow.serializers import ParameterSerializer +from workflow.serializers import ConditionSerializer +from workflow.serializers import MessageSerializer +from workflow.serializers import ValueSerializer +from workflow.serializers import UserSerializer +from workflow.serializers import GroupSerializer -class Transition(viewsets.ModelViewSet): - http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace', 'run'] - queryset = models.Transition.objects.all() - serializer_class = serializers.Transition +class WorkflowViewSet(ModelViewSet): - def get_queryset(self): - queryset=self.queryset - #if 'operation_pk' in self.kwargs: - # import ipdb; ipdb.set_trace() - # queryset = queryset.filter(prerequisites=None) - # return queryset - #if 'prerequisites' in self.request.query_params: - # queryset = queryset.filter(prerequisites=None) - # return queryset - #return queryset.filter(prerequisites__pk=self.kwargs['operation_pk']) - return queryset - - def run(self, request, *args, **kwargs): - - user_args = json.loads(request.body) - context = models.Case.objects.get(pk=user_args.pop('ctx', None)) - if not context: - return Response({ - "status": "404", - "title": "Context Does Not Exist", - "description": "The requested context does not currently exist." - }, status=status.http_404_not_found) + queryset = Workflow.objects.all() + serializer_class = WorkflowSerializer - # TODO Use the following line to run an optimized - # version of `run` that only runs the affected fns. - # Onf faliure here, defer to recreating the available messages - #operation = models.Operation.objects.get(pk=kwargs['pk']) +class CaseViewSet(ModelViewSet): - previous_context = None - while not previous_context == context.values: # This is ugly. - previous_context = copy.deepcopy(context.values) - # Delete all current messages. - context.messages.all().delete() - context.save() - - # Rebuild all messages from tree. - for workflow in context.workflows.all(): - context - if utils.run(context, user_args, workflow.resolver): - print("WORKFLOW COMPLETED") - context.messages.all().delete() - message = models.Message() - message.message_type = 'Notification' - message.response = None - message.content = 'Thank you for completing the workflow.' - message.ctx = context - message.save() - - return Response("Success") - + queryset = Case.objects.all() + serializer_class = CaseSerializer + def perform_create(self, serializer): + case = serializer.save() + case.workflow.update(case) -class TransitionRelationship(RelationshipView): - queryset = models.Transition.objects.all() - serializer_class = models.Transition +class ActionViewSet(ModelViewSet): -class Location(viewsets.ModelViewSet): - queryset = models.Location.objects.all() - serializer_class = serializers.Location + queryset = Action.objects.all() + serializer_class = ActionSerializer -class Arc(viewsets.ModelViewSet): - queryset = models.Arc.objects.all() - serializer_class = serializers.Arc +class ParameterViewSet(ModelViewSet): + queryset = Parameter.objects.all() + serializer_class = ParameterSerializer -class Token(viewsets.ModelViewSet): - queryset = models.Token.objects.all() - serializer_class = serializers.Token - def perform_create(self, serializer): +class ConditionViewSet(ModelViewSet): - token = serializer.save() - token.net.wake() + queryset = Condition.objects.all() + serializer_class = ConditionSerializer -class Case(viewsets.ModelViewSet): - queryset = models.Case.objects.all() - serializer_class = serializers.Case +class MessageViewSet(ModelViewSet): - def perform_create(self, serializer): + queryset = Message.objects.all() + serializer_class = MessageSerializer - case = serializer.save() + def get_queryset(self): + queryset = self.queryset + case = self.request.query_params.get('filter[case]', None) + if case is not None: + queryset = queryset.filter(case=case) + return queryset - # Set up tokens in net's default token configuration - for token in case.net.starting_tokens.all(): - models.Token( - color=token.color, - case=case, - location=token.location, - name=token.name - ).save() - - - case.net.wake() - - # [{ - # 'name': 'Editor Role', - # 'color': 'Editor', - #}, { - # 'name': 'Reviewer Role', - # 'color': 'Reviewer', - #}, { - # 'invited_editors': [], - # 'invited_reviewers': [], - # 'active_editors': [], - # 'active_reviewers': [], - # 'finished_editors': [], - # 'finished_reviewers': [], - # 'review_complete': False, - # 'editing_complete': False, - # 'editor_count': 1, - # 'reviewer_count': 1, - # 'indicated_disciplines': [], - # 'connection_methods': [ - # 'upload', - # 'connect_existing' - # ], - # 'preprint_title': False - #}]: - - def perform_update(self, serializer): - case = serializer.save() +class ValueViewSet(ModelViewSet): - # Initialize any tokens that are IO - for location in self.locations.all(): - if location.type == "IO": - pass - #Token(color=None, case=self, location=location, name='mytoken').save() + queryset = Value.objects.all() + serializer_class = ValueSerializer - case.net.wake() +class UserViewSet(ModelViewSet): -class Message(viewsets.ModelViewSet): - queryset = models.Message.objects.all() - serializer_class = serializers.Message + queryset = User.objects.all() + serializer_class = UserSerializer - def get_queryset(self): - queryset = self.queryset - return queryset +class GroupViewSet(ModelViewSet): -class User(viewsets.ModelViewSet): - queryset = User.objects.all() - serializer_class = serializers.User + queryset = Group.objects.all() + serializer_class = GroupSerializer -class Group(viewsets.ModelViewSet): - queryset = Group.objects.all() - serializer_class = serializers.Group +# diff --git a/service/workflows.json b/service/workflows.json deleted file mode 100644 index 285212f..0000000 --- a/service/workflows.json +++ /dev/null @@ -1,12 +0,0 @@ -[ -{ - "model": "workflow.workflow", - "pk": "61c02575-ef04-4eff-bf98-cc93098eab9e", - "fields": { - "name": "Example Workflow", - "description": "This is an example workflow", - "group": 1, - "resolver": "c00c2979-836d-42ce-8055-f40b4541faba" - } -} -] diff --git a/submission_client/app/adapters/application.js b/submission_client/app/adapters/application.js index 2ce8953..0971256 100644 --- a/submission_client/app/adapters/application.js +++ b/submission_client/app/adapters/application.js @@ -8,6 +8,8 @@ export default JSONAPIAdapter.extend(DS.BuildURLMixin, { namespace: 'api', host: ENV.apiBaseUrl, + + ajax(url, method, hash) { hash = hash || {}; hash.crossDomain = true; diff --git a/submission_client/app/adapters/data.js b/submission_client/app/adapters/data.js new file mode 100644 index 0000000..2275b91 --- /dev/null +++ b/submission_client/app/adapters/data.js @@ -0,0 +1,30 @@ +import Ember from 'ember'; +import DS from 'ember-data'; +import ENV from '../config/environment'; +import JSONAPIAdapter from 'ember-data/adapters/json-api'; + +export default JSONAPIAdapter.extend(DS.BuildURLMixin, { + + namespace: 'api', + host: ENV.apiBaseUrl, + + + + ajax(url, method, hash) { + hash = hash || {}; + hash.crossDomain = true; + hash.xhrFields = { withCredentials: true }; + return this._super(url, method, hash); + }, + + pathForType(type) { + var inflector = new Ember.Inflector(Ember.Inflector.defaultRules); + return Ember.String.underscore(type); + }, + + run(type, id, parameters) { + let url = this.buildURL(type.modelName, id, null, null, null); + return this.ajax(url, 'RUN', { data: parameters }); + } + +}); diff --git a/submission_client/app/components/text-field/component.js b/submission_client/app/components/text-field/component.js index 9fece48..f4b859e 100644 --- a/submission_client/app/components/text-field/component.js +++ b/submission_client/app/components/text-field/component.js @@ -7,22 +7,19 @@ export default Ember.Component.extend({ preprintTitleObserver: Ember.observer('preprintTitle', async function() { const message = this.get('message'); - const locaxion = this.get('message.response'); + const parameter = this.get('message.response'); const caxe = this.get('message.caxe'); this.get('store').findRecord('case', this.get('message.caxe.id')).then(async (caxeo)=>{ - const refresh = this.attrs.refresh; const store = this.get("store"); - const token = store.createRecord('token'); - token.set('color', this.get('preprintTitle')); - token.set('caxe', caxe); - token.set('net', caxeo.get('net')); - token.set('name', message.get('responseTokenName')) - token.set('location', locaxion); - token.set('requestMessage', message); - const response = await token.save(); + const value = store.createRecord('value'); + value.set('value', this.get('preprintTitle')); + value.set('caxe', caxe); + value.set('state', 'defined'); + value.set('parameter', parameter); + const response = await value.save(); this.get('store').findAll('message', {reload: true}).then((messages) => { this.attrs.refresh() }) diff --git a/submission_client/app/components/upload-file/component.js b/submission_client/app/components/upload-file/component.js index 0407fda..dd7068d 100644 --- a/submission_client/app/components/upload-file/component.js +++ b/submission_client/app/components/upload-file/component.js @@ -22,7 +22,7 @@ export default Ember.Component.extend({ const f = event.target.files[0]; const uri = "https://files.osf.io/v1/resources/h8d72/providers/osfstorage/?kind=file&name=" + f.name; const message = this.get('message'); - const locaxion = this.get('message.response'); + const parameter = this.get('message.response'); const caxe = this.get('message.caxe'); this.get('store').findRecord('case', this.get('message.caxe.id')).then((caxeo)=>{ @@ -36,14 +36,12 @@ export default Ember.Component.extend({ xhr.withCredentials = false; xhr.onreadystatechange = async function() { if (xhr.readyState == 4 && xhr.status >= 200 && xhr.status < 300) { - const token = store.createRecord('token'); - token.set('color', JSON.parse(xhr.responseText).data.links.download); - token.set('caxe', caxe); - token.set('net', caxeo.get('net')); - token.set('name', message.get('responseTokenName')); - token.set('location', locaxion); - token.set('requestMessage', message); - await token.save(); + const value = store.createRecord('value'); + value.set('value', JSON.parse(xhr.responseText).data.links.download); + value.set('caxe', caxe); + value.set('state', 'defined'); + value.set('parameter', parameter); + await value.save(); refresh(); } }; diff --git a/submission_client/app/controllers/application.js b/submission_client/app/controllers/application.js index ab612ed..b38e534 100644 --- a/submission_client/app/controllers/application.js +++ b/submission_client/app/controllers/application.js @@ -12,7 +12,6 @@ export default Ember.Controller.extend(OsfAgnosticAuthControllerMixin,{ actions: { loginSuccess() { - // this.transitionToRoute('researcher.grant'); }, loginFail(/* err */) { this.get('toast').error('Login failed'); diff --git a/submission_client/app/controllers/cases/case/inbox/index.js b/submission_client/app/controllers/cases/case/inbox/index.js new file mode 100644 index 0000000..0691f0c --- /dev/null +++ b/submission_client/app/controllers/cases/case/inbox/index.js @@ -0,0 +1,51 @@ +import Ember from "ember"; + +export default Ember.Controller.extend({ + + sections: [ + "upload", + "disciplines", + "basics", + "authors", + "submit" + ], + + actions: { + + deleteMessage: async function(message) { + message.destroyRecord(); + }, + + async refresh() { + const fetched_messages = await this.get("store").query("message", { + filter: { + case: this.get('caxe.id') + } + }); + const current_messages = this.get("messages") + fetched_messages.filter((fetched_message) => { + var exists = false; + current_messages.forEach((current_message) => { + if (fetched_message.id === current_message.id) { + exists = true; + } + }) + return !exists; + }).forEach((new_message) => { + current_messages.pushObject(new_message); + }); + current_messages.filter((current_message) => { + var exists = false; + fetched_messages.forEach((fetched_message) => { + if (fetched_message.id === current_message.id) { + exists = true; + } + }) + return !exists; + }).forEach((stale_message) => { + current_messages.removeObject(stale_message); + }); + } + } + +}); diff --git a/submission_client/app/controllers/index.js b/submission_client/app/controllers/index.js index ecc11b6..e09bcd1 100644 --- a/submission_client/app/controllers/index.js +++ b/submission_client/app/controllers/index.js @@ -4,11 +4,11 @@ export default Ember.Controller.extend({ actions: { - beginWorkflow: async function(net) { + beginWorkflow: async function(workflow) { let wfcase = this.get('store').createRecord('case'); - wfcase.set("net", net) + wfcase.set("workflow", workflow) await wfcase.save(); - this.transitionToRoute('inbox.index'); + this.transitionToRoute('cases.case.inbox', wfcase.id) } } diff --git a/submission_client/app/models/action.js b/submission_client/app/models/action.js new file mode 100644 index 0000000..9927f73 --- /dev/null +++ b/submission_client/app/models/action.js @@ -0,0 +1,24 @@ +import DS from 'ember-data'; + +export default DS.Model.extend({ + + name: DS.attr('string'), + description: DS.attr('string'), + group: DS.belongsTo('group', { + async: true + }), + action_function: DS.attr('string'), + output: DS.belongsTo('parameter', { + async: true + }), + conditions: DS.hasMany('condition', { + inverse: 'action', + async: true + }), + messages: DS.hasMany('message', { + inverse: 'origin', + async: true + }) + +}); + diff --git a/submission_client/app/models/case.js b/submission_client/app/models/case.js index ef90cdc..d91b5ae 100644 --- a/submission_client/app/models/case.js +++ b/submission_client/app/models/case.js @@ -2,9 +2,23 @@ import DS from 'ember-data'; export default DS.Model.extend({ - net: DS.belongsTo('net', {inverse: 'cases'}), - messages: DS.hasMany("message"), - tokens: DS.hasMany('tokens', {inverse: 'caxe'}) + parameters: DS.hasMany('parameter', { + inverse: 'cases', + async: true + }), + values: DS.hasMany('value', { + inverse: 'caxe', + async: true + }), + workflow: DS.belongsTo('workflow', { + inverse: 'cases', + async: true + }), + initialState: DS.attr(), + messages: DS.hasMany('message', { + inverse: 'caxe', + async: true + }), }); diff --git a/submission_client/app/models/condition.js b/submission_client/app/models/condition.js new file mode 100644 index 0000000..eea1510 --- /dev/null +++ b/submission_client/app/models/condition.js @@ -0,0 +1,21 @@ +import DS from 'ember-data'; + +export default DS.Model.extend({ + + parameter: DS.belongsTo('parameter', { + inverse: 'conditions', + async: true + }), + state: DS.attr('string'), + type: DS.attr('string'), + action: DS.belongsTo('action', { + inverse: 'conditions', + async: true + }), + workflow: DS.belongsTo('workflow', { + inverse: 'conditions', + async: true + }) + +}); + diff --git a/submission_client/app/models/location.js b/submission_client/app/models/location.js deleted file mode 100644 index 73f1f22..0000000 --- a/submission_client/app/models/location.js +++ /dev/null @@ -1,16 +0,0 @@ -import DS from 'ember-data'; - -export default DS.Model.extend({ - - name: DS.attr("string"), - description: DS.attr("string"), - type: DS.attr("string"), - sources: DS.hasMany("transitions", {inverse: "outputs"}), - tokens: DS.hasMany('tokens', {inverse: 'location'}), - targets: DS.hasMany("transitions", {inverse: "inputs"}), - sourceOperation: DS.belongsTo("operation"), - caller: DS.hasMany("message", {inverse: "response"}), - net: DS.belongsTo("net", {inverse: "locations"}) - -}); - diff --git a/submission_client/app/models/message.js b/submission_client/app/models/message.js index 4184e40..df929af 100644 --- a/submission_client/app/models/message.js +++ b/submission_client/app/models/message.js @@ -4,14 +4,21 @@ export default DS.Model.extend({ messageType: DS.attr('string'), timestamp: DS.attr('date'), - origin: DS.hasMany('transition', {inverse: 'messages'}), - response: DS.belongsTo('location', {inverse: "caller"}), - caxe: DS.belongsTo('case', {inverse: "messages"}), + view: DS.attr('string'), + caxe: DS.belongsTo('case', { + inverse: 'messages', + async: true + }), + origin: DS.hasMany('action', { + inverse: 'messages', + async: true + }), + response: DS.belongsTo('parameter', { + inverse: "messages", + async: true + }), content: DS.attr('string'), - view: DS.attr(), section: DS.attr('string'), - responseTokens: DS.hasMany('token', {inverse: 'requestMessages'}), - responseTokenName: DS.attr('string') }); diff --git a/submission_client/app/models/net.js b/submission_client/app/models/net.js index 86ed80f..1f6e195 100644 --- a/submission_client/app/models/net.js +++ b/submission_client/app/models/net.js @@ -5,8 +5,14 @@ export default DS.Model.extend({ name: DS.attr('string'), description: DS.attr('string'), group: DS.belongsTo("group"), - transitions: DS.hasMany('transition', {inverse: 'net'}), - locations: DS.hasMany('location', {inverse: 'net'}), + transitions: DS.hasMany('transition', { + inverse: 'net', + async: true + }), + locations: DS.hasMany('location', { + inverse: 'net', + async: true + }), cases: DS.hasMany("case") }); diff --git a/submission_client/app/models/parameter.js b/submission_client/app/models/parameter.js new file mode 100644 index 0000000..f413363 --- /dev/null +++ b/submission_client/app/models/parameter.js @@ -0,0 +1,29 @@ +import DS from 'ember-data'; + +export default DS.Model.extend({ + + name: DS.attr('string'), + view: DS.attr('string'), + cases: DS.hasMany('cases', { + inverse: 'parameters', + async: true + }), + values: DS.hasMany('value', { + inverse: 'parameter', + async: true, + }), + sources: DS.hasMany('action', { + inverse: 'output', + async: true + }), + messages: DS.hasMany('message', { + inverse: 'response', + async: true + }), + conditions: DS.hasMany('condition', { + inverse: 'parameter', + async: true + }) + +}); + diff --git a/submission_client/app/models/token.js b/submission_client/app/models/token.js index 7b50221..f131148 100644 --- a/submission_client/app/models/token.js +++ b/submission_client/app/models/token.js @@ -3,10 +3,19 @@ import DS from 'ember-data'; export default DS.Model.extend({ color: DS.attr(), - caxe: DS.belongsTo('case', {inverse: "tokens"}), - location: DS.belongsTo('location', {inverse: "tokens"}), + caxe: DS.belongsTo('case', { + inverse: "tokens", + async: true + }), + location: DS.belongsTo('location', { + inverse: "tokens", + async: true + }), name: DS.attr('string'), - requestMessages: DS.hasMany("message", {inverse: "responseTokens"}), + requestMessages: DS.hasMany("message", { + inverse: "responseTokens", + async: true + }), net: DS.belongsTo('net') }); diff --git a/submission_client/app/models/transition.js b/submission_client/app/models/transition.js deleted file mode 100644 index d1056cd..0000000 --- a/submission_client/app/models/transition.js +++ /dev/null @@ -1,19 +0,0 @@ -import DS from 'ember-data'; - -export default DS.Model.extend({ - - name: DS.attr("string"), - description: DS.attr("string"), - group: DS.belongsTo("group"), - operation: DS.attr("string"), - args: DS.hasMany("value", {inverse: "operations"}), - returnValue: DS.belongsTo("value", {inverse: "sourceOperation"}), - view: DS.attr("string"), - section: DS.attr("string"), - messages: DS.hasMany("message", {inverse: "origin"}), - net: DS.belongsTo("net", {inverse: "transitions"}), - outputs: DS.hasMany("location", {inverse: "sources"}), - inputs: DS.hasMany("location", {inverse: "targets"}), - -}); - diff --git a/submission_client/app/models/value.js b/submission_client/app/models/value.js new file mode 100644 index 0000000..f8eae40 --- /dev/null +++ b/submission_client/app/models/value.js @@ -0,0 +1,18 @@ +import DS from 'ember-data'; + +export default DS.Model.extend({ + + color: DS.attr(), + caxe: DS.belongsTo('case', { + inverse: 'values', + async: true + }), + parameter: DS.belongsTo('parameter', { + inverse: 'values', + async: true, + }), + state: DS.attr('string'), + value: DS.attr() + +}); + diff --git a/submission_client/app/models/workflow.js b/submission_client/app/models/workflow.js new file mode 100644 index 0000000..221ce74 --- /dev/null +++ b/submission_client/app/models/workflow.js @@ -0,0 +1,15 @@ +import DS from 'ember-data'; + +export default DS.Model.extend({ + + name: DS.attr('string'), + description: DS.attr('string'), + group: DS.belongsTo("group"), + cases: DS.hasMany("case"), + conditions: DS.hasMany('condition', { + inverse: 'workflow', + async: true + }) + +}); + diff --git a/submission_client/app/router.js b/submission_client/app/router.js index 6ecd0ca..788344d 100644 --- a/submission_client/app/router.js +++ b/submission_client/app/router.js @@ -14,12 +14,26 @@ Router.map(function() { }); this.route('workflows', function() { - this.route('workflow', {path: ':workflow'}, function() { + this.route('workflow', { + path: ':workflow' + }, function() { this.route('graph', {path: 'graph'}); }); this.route('create'); }); + this.route('cases', function() { + this.route('case', { + path: ':case' + }, function() { + this.route('inbox', function() { + this.route('index', { + path: '' + }); + }); + }); + }) + this.route('roles', function() { this.route('roles', {path: ':role'}); }); diff --git a/submission_client/app/routes/application.js b/submission_client/app/routes/application.js index 02c3f3d..e345485 100644 --- a/submission_client/app/routes/application.js +++ b/submission_client/app/routes/application.js @@ -1,15 +1,15 @@ import Ember from 'ember'; -import OsfTokenLoginRouteMixin from 'ember-osf/mixins/osf-token-login-route'; +import OsfAgnosticAuthRouteMixin from 'ember-osf/mixins/osf-agnostic-auth-route'; -export default Ember.Route.extend(OsfTokenLoginRouteMixin, { +export default Ember.Route.extend(OsfAgnosticAuthRouteMixin, { store: Ember.inject.service(), session: Ember.inject.service(), model() { - //if (this.get('session.isAuthenticated')) { - // return this.get('store').findRecord('user', 'me'); - //} + if (this.get('session.isAuthenticated')) { + return this.get('store').findRecord('user', 'me'); + } } }); diff --git a/submission_client/app/routes/cases/case.js b/submission_client/app/routes/cases/case.js new file mode 100644 index 0000000..f7cd346 --- /dev/null +++ b/submission_client/app/routes/cases/case.js @@ -0,0 +1,16 @@ +import Ember from 'ember'; +/* global Freewall */ +//import 'bower_components/freewall/freewall'; +// +export default Ember.Route.extend({ + + model: function(params, transition, queryParams) { + return this.get('store').findRecord('case', params.case, {reload: true}); + }, + + setupController: function(controller, model) { + controller.set('caxe', model) + } + + +}); diff --git a/submission_client/app/routes/cases/case/inbox.js b/submission_client/app/routes/cases/case/inbox.js new file mode 100644 index 0000000..f1717e6 --- /dev/null +++ b/submission_client/app/routes/cases/case/inbox.js @@ -0,0 +1,19 @@ +import Ember from 'ember'; +/* global Freewall */ +//import 'bower_components/freewall/freewall'; +// +export default Ember.Route.extend({ + + model: function(params, transition, queryParams) { + //return this.get('store').query('message', { + //filter: { + return this.modelFor('cases.case').get('messages') + //} + //}); + }, + + setupController: function(controller, model) { + controller.set('messages', model) + } + +}); diff --git a/submission_client/app/routes/cases/case/inbox/index.js b/submission_client/app/routes/cases/case/inbox/index.js new file mode 100644 index 0000000..b5299fb --- /dev/null +++ b/submission_client/app/routes/cases/case/inbox/index.js @@ -0,0 +1,19 @@ +import Ember from 'ember'; +/* global Freewall */ +//import 'bower_components/freewall/freewall'; +// +export default Ember.Route.extend({ + + model: function() { + return { + messages: this.modelFor('cases.case.inbox'), + caxe: this.modelFor('cases.case') + } + }, + + setupController: function(controller, model) { + controller.set('messages', model.messages); + controller.set('caxe', model.caxe); + } + +}); diff --git a/submission_client/app/routes/index.js b/submission_client/app/routes/index.js index 8d27aab..0b30e1c 100644 --- a/submission_client/app/routes/index.js +++ b/submission_client/app/routes/index.js @@ -5,7 +5,7 @@ import Ember from 'ember'; export default Ember.Route.extend({ model() { - return this.get('store').findRecord("net", "1a85108c-387d-47cd-8b47-ca831248bc4c"); + return this.get('store').findRecord("workflow", "c4a2a604-aa56-444b-a450-ef17be5f7e38"); }, setupController: async function(controller, model) { diff --git a/submission_client/app/serializers/case.js b/submission_client/app/serializers/case.js new file mode 100644 index 0000000..22fef05 --- /dev/null +++ b/submission_client/app/serializers/case.js @@ -0,0 +1,4 @@ +export default DS.JSONAPISerializer.extend({ + attrs: { + } +}); diff --git a/submission_client/app/serializers/conditon.js b/submission_client/app/serializers/conditon.js new file mode 100644 index 0000000..22fef05 --- /dev/null +++ b/submission_client/app/serializers/conditon.js @@ -0,0 +1,4 @@ +export default DS.JSONAPISerializer.extend({ + attrs: { + } +}); diff --git a/submission_client/app/serializers/message.js b/submission_client/app/serializers/message.js index dcb6897..b5d8ddb 100644 --- a/submission_client/app/serializers/message.js +++ b/submission_client/app/serializers/message.js @@ -1,7 +1,6 @@ export default DS.JSONAPISerializer.extend({ attrs: { - responseTokens: 'response_tokens', - responseTokenName: 'response_token_name', + messageType: 'message-type', caxe: 'case' } }); diff --git a/submission_client/app/serializers/value.js b/submission_client/app/serializers/value.js new file mode 100644 index 0000000..292a9cd --- /dev/null +++ b/submission_client/app/serializers/value.js @@ -0,0 +1,5 @@ +export default DS.JSONAPISerializer.extend({ + attrs: { + caxe: 'case' + } +}); diff --git a/submission_client/app/templates/cases.hbs b/submission_client/app/templates/cases.hbs new file mode 100644 index 0000000..c24cd68 --- /dev/null +++ b/submission_client/app/templates/cases.hbs @@ -0,0 +1 @@ +{{outlet}} diff --git a/submission_client/app/templates/cases/case.hbs b/submission_client/app/templates/cases/case.hbs new file mode 100644 index 0000000..a597a7c --- /dev/null +++ b/submission_client/app/templates/cases/case.hbs @@ -0,0 +1,10 @@ +

Add Preprint

+ +{{outlet}} diff --git a/submission_client/app/templates/cases/case/inbox.hbs b/submission_client/app/templates/cases/case/inbox.hbs new file mode 100644 index 0000000..3cdcecd --- /dev/null +++ b/submission_client/app/templates/cases/case/inbox.hbs @@ -0,0 +1,2 @@ +{{outlet}} + diff --git a/submission_client/app/templates/cases/case/inbox/index.hbs b/submission_client/app/templates/cases/case/inbox/index.hbs new file mode 100644 index 0000000..cd38777 --- /dev/null +++ b/submission_client/app/templates/cases/case/inbox/index.hbs @@ -0,0 +1,30 @@ +
+ +{{#each sections as |section|}} +
+

{{section}}

+ {{#each messages as |message|}} + {{#if (eq message.section section)}} +
+ + + {{message.section}} + +

{{message.response.name}}

+
{{message.content}}
+ {{component message.view message=message refresh=(action "refresh")}} +
+ {{/if}} + {{/each}} +
+{{/each}} + +
diff --git a/submission_client/app/templates/components/.gitkeep b/submission_client/app/templates/components/.gitkeep deleted file mode 100644 index e69de29..0000000