models.py 56.3 KB
Newer Older
Janez K's avatar
Janez K committed
1
2
3
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
4
from django.conf import settings
Janez K's avatar
Janez K committed
5
6
7
import workflows.library

import time
8
import random
Janez K's avatar
Janez K committed
9
10
11
12
13
14

from picklefield.fields import PickledObjectField

from workflows.thumbs import ThumbnailField

from mothra.settings import DEBUG
Janez K's avatar
Janez K committed
15
from mothra.settings import USE_CONCURRENCY
Janez K's avatar
Janez K committed
16

Janez K's avatar
Janez K committed
17
if USE_CONCURRENCY:
Janez K's avatar
Janez K committed
18
19
    from workflows.tasks import runWidgetAsync, runForLoopIteration

Janez K's avatar
Janez K committed
20
from workflows.tasks import executeWidgetFunction, executeWidgetProgressBar, executeWidgetStreaming, executeWidgetWithRequest, runWidget, executeWidgetPostInteract
21

Janez K's avatar
Janez K committed
22
23
24
class WidgetException(Exception):
    pass

Janez K's avatar
Janez K committed
25
26
27
28
class Connection(models.Model):
    output = models.ForeignKey("Output",related_name="connections")
    input = models.ForeignKey("Input",related_name="connections")
    workflow = models.ForeignKey("Workflow",related_name="connections")
Janez K's avatar
Janez K committed
29

30
31
32
33
34
35
    def export(self):
        d = {}
        d['output_id']=self.output.pk
        d['input_id']=self.input.pk
        return d

36
37
38
39
40
    def import_from_json(self,json_data,input_conversion,output_conversion):
        self.output_id = output_conversion[json_data['output_id']]
        self.input_id = input_conversion[json_data['input_id']]
        self.save()

Janez K's avatar
Janez K committed
41
42
43
44
class Category(models.Model):
    name = models.CharField(max_length=50)
    parent = models.ForeignKey('self',related_name="children",null=True,blank=True)
    user = models.ForeignKey(User,null=True,blank=True,related_name="categories")
Janez K's avatar
Janez K committed
45

Janez K's avatar
Janez K committed
46
    workflow = models.ForeignKey('Workflow',null=True,blank=True,related_name="categories")
47
48
49

    order = models.PositiveIntegerField(default=1)

Janez K's avatar
Janez K committed
50
51
    uid = models.CharField(max_length=250,blank=True,default='')

Janez K's avatar
Janez K committed
52
53
54
55
56
57
58
59
    def update_uid(self):
        import uuid
        if self.uid == '' or self.uid is None:
            self.uid = uuid.uuid4()
            self.save()
        if self.parent:
            self.parent.update_uid()

Janez K's avatar
Janez K committed
60
61
    class Meta:
        verbose_name_plural = "categories"
62
63
        ordering = ('order','name',)

Janez K's avatar
Janez K committed
64
65
66
67
68
69
70
    def __unicode__(self):
        if self.parent is None:
            return unicode(self.name)
        else:
            return unicode(unicode(self.parent)+" :: "+self.name)

class Workflow(models.Model):
dejan's avatar
dejan committed
71
72
73
74
    name = models.CharField(max_length=200,default='Untitled workflow') # a field
    user = models.ForeignKey(User,related_name="workflows") # django relationship (ForeignKey), each Workflow is related to a single User
    public = models.BooleanField(default=False) # a field
    description = models.TextField(blank=True,default='') # a field
Janez K's avatar
Janez K committed
75
76
    widget = models.OneToOneField('Widget',related_name="workflow_link",blank=True,null=True)
    template_parent = models.ForeignKey('Workflow',blank=True,null=True,default=None,on_delete=models.SET_NULL)
Janez K's avatar
Janez K committed
77

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
    def import_from_json(self,json_data,input_conversion,output_conversion):
        self.name = json_data['name']
        self.description = json_data['description']
        self.save()
        for widget in json_data['widgets']:
            w = Widget()
            w.workflow = self
            w.import_from_json(widget,input_conversion,output_conversion)
            if widget['workflow']:
                subwidget_workflow = Workflow()
                subwidget_workflow.user = self.user
                subwidget_workflow.import_from_json(widget['workflow'],input_conversion,output_conversion)
                subwidget_workflow.widget = w
                subwidget_workflow.save()
        for connection in json_data['connections']:
            c = Connection()
            c.workflow = self
            c.import_from_json(connection,input_conversion,output_conversion)

97
98
99
100
101
102
103
104
105
106
107
108
109
    def export(self):
        """ Exports the workflow to a dictionary that can be imported """
        d = {}
        d['name']=self.name
        d['description']=self.description
        d['widgets'] = []
        for w in self.widgets.all():
            d['widgets'].append(w.export())
        d['connections'] = []
        for c in self.connections.all():
            d['connections'].append(c.export())
        return d

Janez K's avatar
Janez K committed
110
    def can_be_streaming(self):
dejan's avatar
dejan committed
111
112
        """ Method checks if workflow can be streamed. Check if there is at least one widget with
        the flag abstract_widget__is_streaming on True.  """
Janez K's avatar
Janez K committed
113
114
115
116
        if self.widgets.filter(abstract_widget__is_streaming=True).count()>0:
            return True
        else:
            return False
Janez K's avatar
Janez K committed
117

Janez K's avatar
Janez K committed
118
    def is_for_loop(self):
dejan's avatar
dejan committed
119
120
        """ Method checks if workflow is a for loop. Checks if at least one widget is 
        type for_input. """
Janez K's avatar
Janez K committed
121
122
123
        if self.widgets.filter(type='for_input').count()>0:
            return True
        else:
Janez K's avatar
Janez K committed
124
125
            return False

dejan's avatar
dejan committed
126
127
128
129
130
131
132
133
    def is_cross_validation(self):
        """ Method checks if workflow is a for loop. Checks if at least one widget is 
        type cv input. """
        if self.widgets.filter(type='cv_input').count()>0:
            return True
        else:
            return False

Janez K's avatar
Janez K committed
134
    def get_ready_to_run(self):
dejan's avatar
dejan committed
135
        """ Method prepares this workflows widgets. Returns a list of widget id-s. """
Janez K's avatar
Janez K committed
136
137
138
139
        widgets = self.widgets.all()
        unfinished_list = []
        for w in widgets:
            if not w.finished and not w.running:
dejan's avatar
dejan committed
140
                """ if widget isn't finished and is not running than true"""
Janez K's avatar
Janez K committed
141
142
143
144
                ready_to_run = True
                connections = self.connections.filter(input__widget=w)
                for c in connections:
                    if not c.output.widget.finished:
dejan's avatar
dejan committed
145
                        """ if widget not finished than true """
Janez K's avatar
Janez K committed
146
147
148
149
150
                        ready_to_run = False
                        break
                if ready_to_run:
                    unfinished_list.append(w.id)
        return unfinished_list
Janez K's avatar
Janez K committed
151

Janez K's avatar
Janez K committed
152
    def get_runnable_widgets(self):
dejan's avatar
dejan committed
153
154
        """ Method is the same as get_ready_to_run method. The difference is only that this method
        returns a list widgets as objects (and not only id-s).  """
Janez K's avatar
Janez K committed
155
156
157
158
159
160
161
162
        widgets = self.widgets.all()
        unfinished_list = []
        for w in widgets:
            if not w.finished and not w.running:
                ready_to_run = True
                connections = self.connections.filter(input__widget=w)
                for c in connections:
                    if not c.output.widget.finished:
163
                        #print c.output.widget
Janez K's avatar
Janez K committed
164
165
166
167
168
169
170
                        ready_to_run = False
                        break
                if ready_to_run:
                    unfinished_list.append(w)
        return unfinished_list

    def run_for_loop(self):
dejan's avatar
dejan committed
171
172
        """ Method runs the workflow for loop. The use of [0] at the end of lines is because
        there can be only one for loop in one workflow. This way we take the first one. """
Janez K's avatar
Janez K committed
173
        #clear for_input and for_output
174
        #print("run_for_loop")
Janez K's avatar
Janez K committed
175
176
177
178
179
        fi = self.widgets.filter(type='for_input')[0]
        fo = self.widgets.filter(type='for_output')[0]
        outer_output = fo.inputs.all()[0].outer_output
        outer_output.value=[]
        outer_output.save()
Janez K's avatar
Janez K committed
180

dejan's avatar
dejan committed
181
182
        input_list = fi.outputs.all()[0].outer_input.value # get all inputs from outer part
        progress_total = len(input_list) # for progress bar
Janez K's avatar
Janez K committed
183
184
        current_iteration = 0
        for i in input_list:
Anze Vavpetic's avatar
Anze Vavpetic committed
185
            #print(i);
dejan's avatar
dejan committed
186
187
188
189
            """ Different parameters on which the widgets are going to be run"""
            fi.unfinish() # resets widgets, (read all widgets.finished=false)
            fo.unfinish() # resets widgets, (read all widgets.finished=false)
            proper_output = fi.outputs.all()[0] # inner output
Janez K's avatar
Janez K committed
190
191
            proper_output.value = i
            proper_output.save()
dejan's avatar
dejan committed
192
            fi.finished=True # set the input widget as finished
Janez K's avatar
Janez K committed
193
            fi.save()
Janez K's avatar
Janez K committed
194
            if not USE_CONCURRENCY or 1==1:
dejan's avatar
dejan committed
195
                """ This if statement is always true. """
Janez K's avatar
Janez K committed
196
197
198
199
                unfinished_list = self.get_runnable_widgets()
                try:
                    while len(unfinished_list)>0:
                        for w in unfinished_list:
dejan's avatar
dejan committed
200
                            w.run(True) # run the widget
Janez K's avatar
Janez K committed
201
                            total = self.widgets.count()
Janez K's avatar
Janez K committed
202
                            completed = self.widgets.filter(finished=True).count()
Janez K's avatar
Janez K committed
203
204
205
206
207
208
                            self.widget.progress = (int)((current_iteration*100.0/progress_total)+(((completed*1.0)/total)*(100/progress_total)))
                            self.widget.save()
                        unfinished_list = self.get_runnable_widgets()
                except:
                    raise
            else:
dejan's avatar
dejan committed
209
                """ This part is never executed  """
Janez K's avatar
Janez K committed
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
                unfinished_list = self.get_runnable_widgets()
                try:
                    statuses = {}
                    total = self.widgets.count()
                    completed = self.widgets.filter(finished=True).count()
                    while len(unfinished_list)>0:
                        for w in unfinished_list:
                            if statuses.has_key(w.pk):
                                if statuses[w.pk].failed():
                                    raise Exception(statuses[w.pk].info[0])
                            else:
                                statuses[w.pk]=runWidgetAsync.delay(w)
                            completed = self.widgets.filter(finished=True).count()
                            if self.widget:
                                self.widget.progress = (int)((current_iteration*100.0/progress_total)+(((completed*1.0)/total)*(100/progress_total)))
                                self.widget.save()
                        is_running=True
                        unfinished_list = self.get_runnable_widgets()
                        while len(unfinished_list)==0 and is_running:
                            unfinished_list = self.get_runnable_widgets()
                            is_running = False
                            for st in statuses.values():
                                if st.status == 'PENDING':
                                    is_running = True
                                else:
                                    st.get()
                                    completed = self.widgets.filter(finished=True).count()
                                    if self.widget:
                                        self.widget.progress = (int)((current_iteration*100.0/progress_total)+(((completed*1.0)/total)*(100/progress_total)))
Janez K's avatar
Janez K committed
239
                                        self.widget.save()
Janez K's avatar
Janez K committed
240
241
242
243
244
                            unfinished_list = self.get_runnable_widgets()
                except:
                    raise
            current_iteration = current_iteration+1

dejan's avatar
dejan committed
245
246
247
    def run_cross_validation(self):
        """ Method runs cross_validation. """
        #clear for_input and for_output
248
        #print("run_cross_validation")
dejan's avatar
dejan committed
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
        import random as rand
        fi = self.widgets.filter(type='cv_input')[0]
        fo = self.widgets.filter(type='cv_output')[0]
        outer_output = fo.inputs.all()[0].outer_output
        outer_output.value=[]
        outer_output.save()

        # get all inputs from outer part
        input_list = fi.outputs.all()[0].outer_input.value 
        input_fold = fi.outputs.all()[1].outer_input.value
        input_seed = fi.outputs.all()[2].outer_input.value

        if input_fold != None:
            #check if we have an input
            input_fold = int(fi.outputs.all()[1].outer_input.value)
        else:
            input_fold = 10
        
        if input_seed != None:
            #check if we have an input
            input_seed = int(fi.outputs.all()[2].outer_input.value)
        else:
271
            input_seed = random.randint(0, 10**9)
dejan's avatar
dejan committed
272

273
274
275
276
277
278
279
280
281
282
        # Special case when reading from a DB
        input_type = input_list.__class__.__name__
        context = None
        if input_type == 'DBContext':
            context = input_list
            input_list = context.orng_tables.get(context.target_table, None)

        if not input_list:
            raise Exception('CrossValidation: Empty input list!')

dejan's avatar
dejan committed
283
284
285
286
        progress_total = len(input_list) # for progress bar
        current_iteration = 0

        # create folds
287
288
289

        folds = []
        if hasattr(input_list, "get_items_ref"):
290
            import orange 
291
            # Orange table on input, so we cannot do slices
292
            indices = orange.MakeRandomIndicesCV(input_list, randseed=input_seed, folds=input_fold, stratified=orange.MakeRandomIndices.Stratified)
293
294
295
            for i in range(input_fold):
                output_train = input_list.select(indices, i, negate=1)
                output_test = input_list.select(indices, i)
Marko Lalovic's avatar
Marko Lalovic committed
296
		#print len(output_train), len(output_test)
297
                folds.append((output_train, output_test))
298
        else:
299
300
            rand.seed(input_seed)
            rand.shuffle(input_list)
301
            folds = [input_list[i::input_fold] for i in range(input_fold)]
dejan's avatar
dejan committed
302
303
304
305
306
307
308
309
310
311

        # pass forward the seed
        proper_output = fi.outputs.all()[2] # inner output
        proper_output.value = input_seed
        proper_output.save()

        # this for loop delets all previous results
        for i in fo.inputs.all():
            if not i.parameter:
                if i.connections.count() > 0:
312
313
                    i.value = []
                    i.save()
dejan's avatar
dejan committed
314
315

        for i in range(len(folds)):
316
317
            #import pdb; pdb.set_trace()
            if hasattr(input_list, "get_items_ref"):
Marko Lalovic's avatar
Marko Lalovic committed
318
319
                output_test = folds[i][1]
                output_train = folds[i][0]
320
321
322
            else:
                output_train = folds[:i] + folds[i+1:]
                output_test = folds[i]
323
324
325
326
327
328
329
330
            if input_type == 'DBContext':
                output_train_obj = context.copy()
                output_train_obj.orng_tables[context.target_table] = output_train
                output_test_obj = context.copy()
                output_test_obj.orng_tables[context.target_table] = output_test
                output_train = output_train_obj
                output_test = output_test_obj

dejan's avatar
dejan committed
331
332
333
334
            """ Different parameters on which the widgets are going to be run"""
            fi.unfinish() # resets widgets, (read all widgets.finished=false)
            fo.unfinish() # resets widgets, (read all widgets.finished=false)
            proper_output = fi.outputs.all()[0] # inner output
335
            proper_output.value = output_train
dejan's avatar
dejan committed
336
337
            proper_output.save()
            proper_output = fi.outputs.all()[1] # inner output
338
            proper_output.value = output_test
dejan's avatar
dejan committed
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
            proper_output.save()
            fi.finished=True # set the input widget as finished
            fi.save()
            if not USE_CONCURRENCY or 1==1:
                """ This if statement is always true. """
                unfinished_list = self.get_runnable_widgets()
                try:
                    while len(unfinished_list)>0:
                        for w in unfinished_list:
                            w.run(True) # run the widget
                            total = self.widgets.count()
                            completed = self.widgets.filter(finished=True).count()
                            self.widget.progress = (int)((current_iteration*100.0/progress_total)+(((completed*1.0)/total)*(100/progress_total)))
                            self.widget.save()
                        unfinished_list = self.get_runnable_widgets()
                except:
                    raise
            current_iteration = current_iteration+1

Janez K's avatar
Janez K committed
358
    def run(self):
Janez K's avatar
Janez K committed
359
        if not USE_CONCURRENCY or not self.widget:
Janez K's avatar
Janez K committed
360
            unfinished_list = self.get_runnable_widgets()
361
            #print unfinished_list
Janez K's avatar
Janez K committed
362
363
364
365
366
367
368
369
370
371
372
373
            try:
                total = self.widgets.count()
                completed = self.widgets.filter(finished=True).count()
                while len(unfinished_list)>0:
                    for w in unfinished_list:
                        w.run(True)
                        #runWidgetAsync.delay(w)
                        completed = self.widgets.filter(finished=True).count()
                        if self.widget:
                            self.widget.progress = (int)(((completed*1.0)/total)*100)
                            self.widget.save()
                    unfinished_list = self.get_runnable_widgets()
374
                    #print unfinished_list
Janez K's avatar
Janez K committed
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
            except:
                raise
        else:
            unfinished_list = self.get_runnable_widgets()
            try:
                statuses = {}
                total = self.widgets.count()
                completed = self.widgets.filter(finished=True).count()
                while len(unfinished_list)>0:
                    for w in unfinished_list:
                        if statuses.has_key(w.pk):
                            if statuses[w.pk].failed():
                                raise Exception(statuses[w.pk].info[0])
                        else:
                            statuses[w.pk]=runWidgetAsync.delay(w)
                        completed = self.widgets.filter(finished=True).count()
                        if self.widget:
                            self.widget.progress = (int)(((completed*1.0)/total)*100)
                            self.widget.save()
                    is_running=True
Janez K's avatar
Janez K committed
395
                    unfinished_list = self.get_runnable_widgets()
Janez K's avatar
Janez K committed
396
397
398
399
400
401
402
403
404
405
406
                    while len(unfinished_list)==0 and is_running:
                        unfinished_list = self.get_runnable_widgets()
                        is_running = False
                        for st in statuses.values():
                            if st.status == 'PENDING':
                                is_running = True
                            else:
                                st.get()
                                completed = self.widgets.filter(finished=True).count()
                                if self.widget:
                                    self.widget.progress = (int)(((completed*1.0)/total)*100)
Janez K's avatar
Janez K committed
407
                                    self.widget.save()
Janez K's avatar
Janez K committed
408
                        time.sleep(1)
Janez K's avatar
Janez K committed
409
                    unfinished_list = self.get_runnable_widgets()
Janez K's avatar
Janez K committed
410
411
412
413
            except:
                raise
    def rename(self,new_name):
        self.name = new_name
Janez K's avatar
Janez K committed
414
415
        self.save()

Janez K's avatar
Janez K committed
416
417
    @models.permalink
    def get_absolute_url(self):
Janez K's avatar
Janez K committed
418
419
        return ('open workflow', [str(self.id)])

Janez K's avatar
Janez K committed
420
421
422
    @models.permalink
    def get_copy_url(self):
        return ('copy workflow', [str(self.id)])
Janez K's avatar
Janez K committed
423

Janez K's avatar
Janez K committed
424
425
426
    @models.permalink
    def get_info_url(self):
        return ('workflow information', [str(self.id)])
Janez K's avatar
Janez K committed
427

428
429
430
431
    @models.permalink
    def get_export_url(self):
        return ('export workflow', [str(self.id)])

Janez K's avatar
Janez K committed
432
433
    def __unicode__(self):
        return unicode(self.name)
Janez K's avatar
Janez K committed
434

Janez K's avatar
Janez K committed
435
    class Meta:
Janez K's avatar
Janez K committed
436
        ordering = ['name']
Janez K's avatar
Janez K committed
437
438

class AbstractWidget(models.Model):
Janez K's avatar
Janez K committed
439
440
    name = models.CharField(max_length=200,help_text='Name is the name that will be displayed in the widget repository and under the actual widget itself.')
    action = models.CharField(max_length=200,help_text='Action is the name of a python function that will be called when the widget is executed.')
Janez K's avatar
Janez K committed
441
    wsdl = models.URLField(max_length=200,blank=True,help_text='WSDL and WSDL method are used if the widget is a call of a Web Service. Web Service widgets are usually not entered in the admin panel, but in the application itself by importing a Web Service.')
Janez K's avatar
Janez K committed
442
    wsdl_method = models.CharField(max_length=200,blank=True,default='')
Janez K's avatar
Janez K committed
443
444
445
    description = models.TextField(blank=True,help_text='Description is used for a human readable description of what a widget does. A user will see this when he right clicks the widget and clicks help.')
    category = models.ForeignKey(Category,related_name="widgets",help_text='Category determines to which category this widget belongs. Categories can be nested.')
    visualization_view = models.CharField(max_length=200,blank=True,default='',help_text='Visualization view is (like the action) a python function that is a view that will render a template.')
446
    streaming_visualization_view = models.CharField(max_length=200,blank=True,default='',help_text='Visualization view is (like the action) a python function that is a view that will render a template.')
Janez K's avatar
Janez K committed
447
448
    user = models.ForeignKey(User,blank=True,null=True,related_name="widgets",help_text='If the User field is blank, everyone will see the widget, otherwise just this user. This is mainly used for Web Service imports as they are only visible to users that imported them.')
    interactive = models.BooleanField(default=False,help_text='The widget can be interactive. This means that when a user executes the widget, the action will perform, then the interaction view will be executed and finally the Post interact action will be executed.')
Janez K's avatar
Janez K committed
449
450
    interaction_view = models.CharField(max_length=200,blank=True,default='')
    post_interact_action = models.CharField(max_length=200,blank=True,default='')
Janez K's avatar
Janez K committed
451

Janez K's avatar
Janez K committed
452
    image = ThumbnailField(blank=True,null=True,upload_to="images",size=(34,34),help_text='Image and Treeview image are deprecated and will be phased out soon. Please use the static image field.')
Janez K's avatar
Janez K committed
453
    treeview_image = ThumbnailField(blank=True,null=True,upload_to="treeview",size=(16,16))
454

Janez K's avatar
Janez K committed
455
    static_image = models.CharField(max_length=250,blank=True,default='',help_text='In the static image field just enter the filename of the image (without the path). The path will be $package_name$/icons/widget/$filename$ and $package_name$/icons/treeview/$filename$ where the treeview image is the small image that appears in the treeview on the left side and the widget image is the actual normal sized icon for the widget. IMPORTANT: the static image field only works if the package is set.')
Janez K's avatar
Janez K committed
456

Janez K's avatar
Janez K committed
457
458
    has_progress_bar = models.BooleanField(default=False,help_text='The flag has progress bar determines if the widget implements a progress bar.')
    is_streaming = models.BooleanField(default=False,help_text='The is streaming flag is currently under construction, please do not use it yet.')
Janez K's avatar
Janez K committed
459

Janez K's avatar
Janez K committed
460
    order = models.PositiveIntegerField(default=1,help_text='The Order determines the order in which the widget will be displayed in the repository. This is set automatically when sorting widgets in a single category from the admin.')
Janez K's avatar
Janez K committed
461

Janez K's avatar
Janez K committed
462
    uid = models.CharField(max_length=250,blank=True,default='',help_text='UID is set automatically when you export a package with the -u switch.')
Janez K's avatar
Janez K committed
463

Janez K's avatar
Janez K committed
464
    package = models.CharField(max_length=150,blank=True,default='',help_text='Package is the package name. You are encouraged to use packages.')
Janez K's avatar
Janez K committed
465

466
467
    windows_queue = models.BooleanField(default=False,help_text="This is used for Matjaz Jursic's widgets.")

Janez K's avatar
Janez K committed
468
469
    class Meta:
        ordering = ('order','name',)
470

Janez K's avatar
Janez K committed
471
472
    def set_uid(self,commit=False):
        import uuid
Janez K's avatar
Janez K committed
473
        self.uid = str(uuid.uuid4())
Janez K's avatar
Janez K committed
474
475
476
        if commit:
            self.save()
        for i in self.inputs.all():
Janez K's avatar
Janez K committed
477
            i.uid = str(uuid.uuid4())
Janez K's avatar
Janez K committed
478
479
            if commit:
                i.save()
bogdan's avatar
bogdan committed
480
            for option in i.options.all():
Janez K's avatar
Janez K committed
481
                option.uid = str(uuid.uuid4())
482
483
                if commit:
                    option.save()
Janez K's avatar
Janez K committed
484
        for o in self.outputs.all():
Janez K's avatar
Janez K committed
485
            o.uid = str(uuid.uuid4())
Janez K's avatar
Janez K committed
486
487
488
            if commit:
                o.save()

Janez K's avatar
Janez K committed
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
    def update_uid(self):
        import uuid
        if self.uid == '' or self.uid is None:
            self.uid = uuid.uuid4()
            self.save()
        for i in self.inputs.filter(uid=''):
            i.uid = uuid.uuid4()
            i.save()
            for option in i.options.filter(uid=''):
                option.uid = uuid.uuid4()
                option.save()
        for o in self.outputs.filter(uid=''):
            o.uid = uuid.uuid4()
            o.save()   
        self.category.update_uid()     

Janez K's avatar
Janez K committed
505
506
507
508
509
510
511
    def __unicode__(self):
        return unicode(self.name)

class AbstractInput(models.Model):
    name = models.CharField(max_length=200)
    short_name = models.CharField(max_length=3)
    description = models.TextField(blank=True)
Janez K's avatar
Janez K committed
512
    variable = models.CharField(max_length=50,help_text='The variable attribute of both the input and the output are important because this is how the data will be accessed in the python function that is executed when the widget runs.')
Janez K's avatar
Janez K committed
513
514
515
    widget = models.ForeignKey(AbstractWidget,related_name="inputs")
    required = models.BooleanField()
    parameter = models.BooleanField()
Janez K's avatar
Janez K committed
516
    multi = models.BooleanField(default=False,help_text='Inputs with this flag set will behave like this: whenever a connection is added to this input another input will be created on the fly that accepts the same data. In the action function, this will be represented as a list.')
Janez K's avatar
Janez K committed
517
518
519
    default = models.TextField(blank=True)
    PARAMETER_CHOICES = (
        ('text','Single line'),
520
        ('password', 'Password'),
Janez K's avatar
Janez K committed
521
522
523
524
525
526
        ('textarea','Multi line text'),
        ('select', 'Select box'),
        ('checkbox', 'Checkbox'),
        ('file', 'File'),
    )
    parameter_type = models.CharField(max_length=50,choices=PARAMETER_CHOICES,blank=True,null=True)
Janez K's avatar
Janez K committed
527

Janez K's avatar
Janez K committed
528
    order = models.PositiveIntegerField(default=1)
Janez K's avatar
Janez K committed
529
530

    uid = models.CharField(max_length=250,blank=True,default='')
Janez K's avatar
Janez K committed
531

Janez K's avatar
Janez K committed
532
533
    def __unicode__(self):
        return unicode(self.name)
Janez K's avatar
Janez K committed
534

Janez K's avatar
Janez K committed
535
536
    class Meta:
        ordering = ('order',)
Janez K's avatar
Janez K committed
537

Janez K's avatar
Janez K committed
538
539
540
541
class AbstractOption(models.Model):
    abstract_input = models.ForeignKey(AbstractInput,related_name="options")
    name = models.CharField(max_length=200)
    value = models.TextField(blank=True)
Janez K's avatar
Janez K committed
542
543
544

    uid = models.CharField(max_length=250,blank=True,default='')

Janez K's avatar
Janez K committed
545
546
    def __unicode__(self):
        return unicode(self.name)
Janez K's avatar
Janez K committed
547

Janez K's avatar
Janez K committed
548
    class Meta:
Janez K's avatar
Janez K committed
549
        ordering = ['name']
Janez K's avatar
Janez K committed
550
551
552
553
554

class AbstractOutput(models.Model):
    name = models.CharField(max_length=200)
    short_name = models.CharField(max_length=3)
    description = models.TextField(blank=True)
Janez K's avatar
Janez K committed
555
    variable = models.CharField(max_length=50,help_text='The variable attribute of both the input and the output are important because this is how the data will be accessed in the python function that is executed when the widget runs.')
Janez K's avatar
Janez K committed
556
    widget = models.ForeignKey(AbstractWidget,related_name="outputs")
Janez K's avatar
Janez K committed
557

Janez K's avatar
Janez K committed
558
    order = models.PositiveIntegerField(default=1)
Janez K's avatar
Janez K committed
559
560

    uid = models.CharField(max_length=250,blank=True,default='')
Janez K's avatar
Janez K committed
561

Janez K's avatar
Janez K committed
562
    class Meta:
Janez K's avatar
Janez K committed
563
564
        ordering = ('order',)

Janez K's avatar
Janez K committed
565
566
567
568
    def __unicode__(self):
        return unicode(self.name)

class Widget(models.Model):
dejan's avatar
dejan committed
569
570
    """ Widget """
    # django relationship (ForeignKey), each widget is related to a single workflow
Janez K's avatar
Janez K committed
571
    workflow = models.ForeignKey(Workflow,related_name="widgets")
dejan's avatar
dejan committed
572
573
574
    x = models.IntegerField() # a field
    y = models.IntegerField() # a field
    name = models.CharField(max_length=200) # a field
Janez K's avatar
Janez K committed
575
    abstract_widget = models.ForeignKey(AbstractWidget,related_name="instances",blank=True,null=True)
dejan's avatar
dejan committed
576
577
578
579
580
    finished = models.BooleanField(default=False) # a field
    error = models.BooleanField(default=False) # a field
    running = models.BooleanField(default=False) # a field
    interaction_waiting = models.BooleanField(default=False) # a field
    """ type of widgets """
Janez K's avatar
Janez K committed
581
582
583
584
585
    WIDGET_CHOICES = (
        ('regular','Regular widget'),
        ('subprocess','Subprocess widget'),
        ('input', 'Input widget'),
        ('output', 'Output widget'),
586
587
        ('for_input', 'For input'),
        ('for_output', 'For output'),
Janez K's avatar
Janez K committed
588
589
        ('cv_input', 'Cross Validation input'),
        ('cv_output', 'Cross Validation output'),
Janez K's avatar
Janez K committed
590
591
        ('cv_input2', 'Cross Validation input 2'),
        ('cv_input3', 'Cross Validation input 3'),
Janez K's avatar
Janez K committed
592
593
    )
    type = models.CharField(max_length=50,choices=WIDGET_CHOICES,default='regular')
Janez K's avatar
Janez K committed
594

Janez K's avatar
Janez K committed
595
    progress = models.IntegerField(default=0)
Janez K's avatar
Janez K committed
596

597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
    def import_from_json(self,json_data,input_conversion,output_conversion):
        self.x = json_data['x']
        self.y = json_data['y']
        self.name = json_data['name']
        if json_data['abstract_widget']:
            aw = AbstractWidget.objects.get(uid=json_data['abstract_widget'],package=json_data['abstract_widget_package'])
            self.abstract_widget = aw
        self.type = json_data['type']
        self.save()
        for i in json_data['inputs']:
            new_i = Input()
            new_i.widget = self
            new_i.import_from_json(i,input_conversion,output_conversion)
        for o in json_data['outputs']:
            new_o = Output()
            new_o.widget = self
            new_o.import_from_json(o,input_conversion,output_conversion)


616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
    def export(self):
        d = {}
        try:
            #d['workflow']=self.workflow.export()
            if self.workflow_link:
                d['workflow']=self.workflow_link.export()
            else:
                d['workflow']=None
        except Workflow.DoesNotExist:
            d['workflow']=None
        d['x']=self.x
        d['y']=self.y
        d['name']=self.name
        if self.abstract_widget:
            if self.abstract_widget.uid:
                d['abstract_widget']=self.abstract_widget.uid
632
                d['abstract_widget_package']=self.abstract_widget.package
633
634
635
636
            else:
                raise Exception("Cannot export a widget that doesn't have a UID. ("+str(self.name)+")")
        else:
            d['abstract_widget']=None
637
638
639
640
        #d['finished']=self.finished
        #d['error']=self.error
        #d['running']=self.running
        #d['interaction_waiting']=self.interaction_waiting
641
        d['type']=self.type
642
        #d['progress']=self.progress
643
644
645
646
647
648
649
650
        d['inputs']=[]
        d['outputs']=[]
        for i in self.inputs.all():
            d['inputs'].append(i.export())
        for o in self.outputs.all():
            d['outputs'].append(o.export())
        return d

Janez K's avatar
Janez K committed
651
652
653
654
655
656
    def is_visualization(self):
        try:
            if self.abstract_widget.visualization_view != '':
                return True
        except:
            return False
Janez K's avatar
Janez K committed
657

Janez K's avatar
Janez K committed
658
659
660
661
662
663
    def ready_to_run(self):
        cons = Connection.objects.filter(input__widget=self)
        for c in cons:
            if not c.output.widget.finished:
                return False
        return True
Janez K's avatar
Janez K committed
664

Janez K's avatar
Janez K committed
665
    def unfinish(self):
Janez K's avatar
Janez K committed
666
        self.reset_descendants()
Janez K's avatar
Janez K committed
667

Janez K's avatar
Janez K committed
668
669
670
671
672
673
674
675
    def subunfinish(self):
        if self.type == 'subprocess':
            for w in self.workflow_link.widgets.all():
                w.finished=False
                w.error = False
                w.save()
                if w.type=='subprocess':
                    w.subunfinish()
Janez K's avatar
Janez K committed
676

Janez K's avatar
Janez K committed
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
    def rename(self,new_name):
        self.name = new_name
        self.save()
        if self.type=='input':
            inp = self.outputs.all()[0]
            inp.short_name = self.name[:3]
            inp.name = self.name
            inp.save()
            inp.outer_input.name = self.name
            inp.outer_input.short_name = self.name[:3]
            inp.outer_input.save()
        if self.type=='output':
            inp = self.inputs.all()[0]
            inp.short_name = self.name[:3]
            inp.name = self.name
            inp.save()
            inp.outer_output.name = self.name
            inp.outer_output.short_name = self.name[:3]
Janez K's avatar
Janez K committed
695
            inp.outer_output.save()
Janez K's avatar
Janez K committed
696
697
698
699
700
701
        try:
            w_link = self.workflow_link
            w_link.name=new_name
            w_link.save()
        except Workflow.DoesNotExist:
            pass
Janez K's avatar
Janez K committed
702

Janez K's avatar
Janez K committed
703
    def run(self,offline):
dejan's avatar
dejan committed
704
        """ This is only a hack, to make this work on windows """
Janez K's avatar
bugfix    
Janez K committed
705
        try: 
706
            if self.abstract_widget.windows_queue and settings.USE_WINDOWS_QUEUE:
Janez K's avatar
bugfix    
Janez K committed
707
708
709
710
711
                t = runWidget.apply_async([self,offline],queue="windows")
                t.wait()
            else:
                self.proper_run(offline)
        except AttributeError:
Janez K's avatar
fix    
Janez K committed
712
713
714
            self.proper_run(offline)

    def proper_run(self,offline):
dejan's avatar
dejan committed
715
        """ This is the real start. """
716
        #print("proper_run_widget")
Janez K's avatar
Janez K committed
717
        if not self.ready_to_run():
Janez K's avatar
Janez K committed
718
            raise WidgetException("The prerequisites for running this widget have not been met.")
Janez K's avatar
Janez K committed
719
720
721
        self.running=True
        self.save()
        if self.type == 'regular' or self.type == 'subprocess':
dejan's avatar
dejan committed
722
            """ if this is a subprocess or a regular widget than true."""
Janez K's avatar
Janez K committed
723
            if not self.abstract_widget is None:
724
                """if this is an abstract widget than true we save the widget function in a variable """
Janez K's avatar
Janez K committed
725
726
727
728
                function_to_call = getattr(workflows.library,self.abstract_widget.action)
            input_dict = {}
            outputs = {}
            for i in self.inputs.all():
dejan's avatar
dejan committed
729
                """ we walk through all the inputs """
Janez K's avatar
Janez K committed
730
731
                #gremo pogledat ce obstaja povezava in ce obstaja gremo value prebrat iz outputa
                if not i.parameter:
dejan's avatar
dejan committed
732
                    """ if there is a connection than true and read the output value """
Janez K's avatar
Janez K committed
733
734
735
736
737
738
739
740
741
742
743
744
745
                    if i.connections.count() > 0:
                        i.value = i.connections.all()[0].output.value
                        i.save()
                    else:
                        i.value = None
                        i.save()
                if i.multi_id == 0:
                    input_dict[i.variable]=i.value
                else:
                    if not i.variable in input_dict:
                        input_dict[i.variable]=[]
                    if not i.value==None:
                        input_dict[i.variable].append(i.value)
Janez K's avatar
Janez K committed
746
            start = time.time()
Janez K's avatar
Janez K committed
747
748
            try:
                if not self.abstract_widget is None:
dejan's avatar
dejan committed
749
750
                    """ again, if this objects is an abstract widget than true and check certain parameters,
                    else check if is_for_loop"""
Janez K's avatar
Janez K committed
751
                    if self.abstract_widget.wsdl != '':
dejan's avatar
dejan committed
752
                        """ if abstrac widget is a web service """
Janez K's avatar
Janez K committed
753
754
755
                        input_dict['wsdl']=self.abstract_widget.wsdl
                        input_dict['wsdl_method']=self.abstract_widget.wsdl_method
                    if self.abstract_widget.has_progress_bar:
dejan's avatar
dejan committed
756
                        """ if abstrac widget has a progress bar """
Janez K's avatar
fix    
Janez K committed
757
                        outputs = function_to_call(input_dict,self)
Janez K's avatar
Janez K committed
758
                    elif self.abstract_widget.is_streaming:
dejan's avatar
dejan committed
759
                        """ if abstrac widget is a stream """
Janez K's avatar
fix    
Janez K committed
760
                        outputs = function_to_call(input_dict,self,None)
Janez K's avatar
Janez K committed
761
                    else:
dejan's avatar
dejan committed
762
                        """ else run abstract widget function """
Janez K's avatar
fix    
Janez K committed
763
                        outputs = function_to_call(input_dict)
Janez K's avatar
Janez K committed
764
765
                else:
                    if self.workflow_link.is_for_loop():
dejan's avatar
dejan committed
766
767
                        """ if this is object is a for loop than true and run;
                        else false and run workflow """
768
                        #print("proper_run_is_for_loop")
Janez K's avatar
Janez K committed
769
                        self.workflow_link.run_for_loop()
770
                        #print self.outputs.all()[0].value
dejan's avatar
dejan committed
771
772
                    elif self.workflow_link.is_cross_validation():
                        self.workflow_link.run_cross_validation()
Janez K's avatar
Janez K committed
773
774
775
776
777
778
779
780
                    else:
                        self.workflow_link.run()
            except:
                self.error=True
                self.running=False
                self.finished=False
                self.save()
                raise
Janez K's avatar
Janez K committed
781
782
            elapsed = (time.time()-start)
            outputs['clowdflows_elapsed']=elapsed
Janez K's avatar
Janez K committed
783
            for o in self.outputs.all():
dejan's avatar
dejan committed
784
                """ we walk through all the outputs """
Janez K's avatar
Janez K committed
785
                if not self.abstract_widget is None:
dejan's avatar
dejan committed
786
787
                    """ if this object is an abstract widget than true and save output
                    else look for outputs in workflow """
Janez K's avatar
Janez K committed
788
789
790
791
                    try:
                        o.value = outputs[o.variable]
                    except:
                        pass
Janez K's avatar
Janez K committed
792
793
794
795
796
797
798
                    o.save()
                else:
                    #gremo v outpute pogledat
                    if not self.workflow_link.is_for_loop():
                        o.value = o.inner_input.value
                        o.save()
            if self.abstract_widget is None:
dejan's avatar
dejan committed
799
                """ if object is widget than true and configure parameters """
Janez K's avatar
Janez K committed
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
                self.finished=True
                self.running=False
                self.error=False
                self.save()
            else:
                if not self.abstract_widget.interactive or offline:
                    self.finished=True
                    self.running=False
                    self.error=False
                    self.save()
            cons = Connection.objects.filter(output__widget=self)
            for c in cons:
                c.input.widget.unfinish()
            return outputs
        elif self.type == 'for_input':
dejan's avatar
dejan committed
815
816
            """ if object is an input widget for for loop than read all input values and finish """
            #print("for_input")
Janez K's avatar
Janez K committed
817
818
            for o in self.outputs.all():
                o.value=o.outer_input.value
dejan's avatar
dejan committed
819
                #print(o.outer_input.value)
Janez K's avatar
Janez K committed
820
821
822
823
824
825
                o.save()
            self.finished=True
            self.running=False
            self.error=False
            self.save()
        elif self.type == 'for_output':
dejan's avatar
dejan committed
826
827
828
            """ if object is an output widget for for loop, then read output values and 
            configure parameters"""
            #print("for_output")
Janez K's avatar
Janez K committed
829
830
            for i in self.inputs.all():
                if not i.parameter:
dejan's avatar
dejan committed
831
                    """ if there is a connection than true and read the output value """
Janez K's avatar
Janez K committed
832
833
834
835
836
837
838
839
840
841
                    if i.connections.count() > 0:
                        i.value = i.connections.all()[0].output.value
                        i.save()
                        i.outer_output.value.append(i.value)
                        i.outer_output.save()
                        self.finished=True
            self.finished=True
            self.running=False
            self.error=False
            self.save()
dejan's avatar
dejan committed
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
        elif self.type == 'cv_input':
            """ if object is an input widget for cross validation 
            than read all input values and finish """
            for o in self.outputs.all():
                #print('cv_input')
                o.value=o.outer_input.value
                o.save()
            self.finished=True
            self.running=False
            self.error=False
            self.save()
        elif self.type == 'cv_output':
            """ if object is an output widget for cross validation, 
            then read output values and configure parameters"""
            for i in self.inputs.all():
                if not i.parameter:
                    """ if there is a connection than true and read the output value """
                    if i.connections.count() > 0:
                        if i.value is None:
                            i.value = [i.connections.all()[0].output.value]
                        else:
                            i.value = [i.connections.all()[0].output.value] + i.value
                        #print i.value
                        i.save()
                        i.outer_output.value.append(i.value)
                        i.outer_output.save()
                        self.finished=True
            self.finished=True
            self.running=False
            self.error=False
            self.save()
Janez K's avatar
Janez K committed
873
        elif self.type == 'input':
dejan's avatar
dejan committed
874
            """ if object is an input widget for for loop than read all input values and finish """
Janez K's avatar
Janez K committed
875
876
877
878
879
880
881
882
            for o in self.outputs.all():
                o.value=o.outer_input.value
                o.save()
            self.finished=True
            self.running=False
            self.error=False
            self.save()
        elif self.type == 'output':
dejan's avatar
dejan committed
883
884
            """ if object is an output widget, then read output values and 
            configure parameters"""
Janez K's avatar
Janez K committed
885
886
            for i in self.inputs.all():
                if not i.parameter:
dejan's avatar
dejan committed
887
                    """ if there is a connection than true and read the output value """
Janez K's avatar
Janez K committed
888
889
890
891
892
893
                    if i.connections.count() > 0:
                        i.value = i.connections.all()[0].output.value
                        i.save()
                        i.outer_output.value = i.value
                        i.outer_output.save()
                        self.finished=True
Janez K's avatar
Janez K committed
894
            self.finished=True
Janez K's avatar
Janez K committed
895
896
            self.running=False
            self.error=False
897
            self.save()
Janez K's avatar
Janez K committed
898
        return None
Janez K's avatar
Janez K committed
899

900
    def reset(self,offline):
Janez K's avatar
Janez K committed
901
902
903
904
905
906
907
        #for i in self.inputs.all():
        #    if not i.parameter:
        #        i.value = None
        #        i.save()
        #for i in self.outputs.all():
        #    i.value = None
        #    i.save()
908
909
910
911
        self.finished = False
        self.error = False
        self.running = False
        self.save()
Janez K's avatar
Janez K committed
912
913
        if self.type == 'subprocess':
            self.subunfinish()
914

Janez K's avatar
Janez K committed
915
    def reset_descendants(self):
dejan's avatar
dejan committed
916
        """ Method resets all the widget connections/descendants. """
Janez K's avatar
Janez K committed
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
        pairs = []
        for c in self.workflow.connections.select_related("output","input").defer("output__value","input__value").all():
            if not (c.output.widget_id,c.input.widget_id) in pairs:
                pairs.append((c.output.widget_id,c.input.widget_id))
        next = {}                
        for p in pairs:
            if not next.has_key(p[0]):
                next[p[0]]=set()
            next[p[0]].add(p[1])
        widgets_that_need_reset = set([self.pk,])
        current_widgets_that_need_reset = set([self.pk,])
        while len(current_widgets_that_need_reset)>0:
            new_widgets_that_need_reset = set()
            for w_id in current_widgets_that_need_reset:
                try:
                    for p in next.get(w_id):
                        new_widgets_that_need_reset.add(p)
                        widgets_that_need_reset.add(p)
                except:
                    pass
            current_widgets_that_need_reset = new_widgets_that_need_reset
        Widget.objects.filter(id__in=widgets_that_need_reset).update(finished=False,error=False,running=False)
        subprocesses = Widget.objects.filter(id__in=widgets_that_need_reset,type='subprocess')
        for w in subprocesses:
            w.subunfinish()
        return widgets_that_need_reset

    def reset_descendants_slow(self):
Janez K's avatar
Janez K committed
945
        #find all descendants and reset them as well
Janez K's avatar
Janez K committed
946
        widgets = list(self.workflow.widgets.prefetch_related('inputs','outputs','inputs__connections','outputs__connections','outputs__connections__input','inputs__connections__output'))
Janez K's avatar
Janez K committed
947
948
949
950
951
952
953
954
955
956
957
958
959
        widgets_dict = {}
        widgets_that_need_reset = set([self.pk,])
        current_widgets_that_need_reset = set([self.pk,])
        for w in widgets:
            widgets_dict[w.pk]=w
        while len(current_widgets_that_need_reset)>0:
            new_widgets_that_need_reset = set()
            for w_id in current_widgets_that_need_reset:
                for o in widgets_dict[w_id].outputs.all():
                    for c in o.connections.all():
                        new_widgets_that_need_reset.add(c.input.widget_id)
                        widgets_that_need_reset.add(c.input.widget_id)
            current_widgets_that_need_reset = new_widgets_that_need_reset
Janez K's avatar
Janez K committed
960
        Widget.objects.filter(id__in=widgets_that_need_reset).update(finished=False,error=False,running=False)
Janez K's avatar
Janez K committed
961
        for w in widgets_that_need_reset:
Janez K's avatar
Janez K committed
962
963
964
            if widgets_dict[w].type == 'subprocess':
                widgets_dict[w].subunfinish()
        #    widgets_dict[w].reset(False)
Janez K's avatar
Janez K committed
965
966
        return widgets_that_need_reset

Janez K's avatar
Janez K committed
967
968
    def run_post(self,request):
        if not self.ready_to_run():
Janez K's avatar
Janez K committed
969
            raise WidgetException("The prerequisites for running this widget have not been met.")
Janez K's avatar
Janez K committed
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
        self.running=True
        self.save()
        function_to_call = getattr(workflows.library,self.abstract_widget.post_interact_action)
        input_dict = {}
        outputs = {}
        output_dict = {}
        for o in self.outputs.all():
            output_dict[o.variable]=o.value
        for i in self.inputs.all():
            #gremo pogledat ce obstaja povezava in ce obstaja gremo value prebrat iz outputa
            if not i.parameter:
                if i.connections.count() > 0:
                    i.value = i.connections.all()[0].output.value
                    i.save()
                else:
                    i.value = None
                    i.save()
            if i.multi_id == 0:
                input_dict[i.variable]=i.value
            else:
                if not i.variable in input_dict:
                    input_dict[i.variable]=[]
                if not i.value==None:
                    input_dict[i.variable].append(i.value)
        try:
            if not self.abstract_widget is None:
996
                if self.abstract_widget.windows_queue and settings.USE_WINDOWS_QUEUE:
Janez K's avatar
Janez K committed
997
                    t = executeWidgetPostInteract.apply_async([self,input_dict,output_dict,request],queue="windows")
998
999
                    outputs = t.wait()
                else:
Janez K's avatar
Janez K committed
1000
                    outputs = executeWidgetPostInteract(self,input_dict,output_dict,request)
Janez K's avatar
Janez K committed
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
            else:
                self.workflow_link.run()
        except:
            self.error=True
            self.running=False
            self.finished=False
            self.save()
            raise
        for o in self.outputs.all():
            o.value = outputs[o.variable]
            o.save()
        self.finished=True
        self.running=False
        self.error=False
        self.interaction_waiting=False
        self.save()
        cons = Connection.objects.filter(output__widget=self)
        for c in cons:
            c.input.widget.unfinish()
Janez K's avatar
Janez K committed
1020
1021
        return outputs

Janez K's avatar
Janez K committed
1022
1023
    def __unicode__(self):
        return unicode(self.name)
Janez K's avatar
Janez K committed
1024

Janez K's avatar
Janez K committed
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
class Input(models.Model):
    name = models.CharField(max_length=200)
    short_name = models.CharField(max_length=3)
    description = models.TextField(blank=True,null=True)
    variable = models.CharField(max_length=50)
    widget = models.ForeignKey(Widget,related_name="inputs")
    required = models.BooleanField()
    parameter = models.BooleanField()
    value = PickledObjectField(null=True)
    multi_id = models.IntegerField(default=0)
    inner_output = models.ForeignKey('Output',related_name="outer_input_rel",blank=True,null=True) #za subprocess
    outer_output = models.ForeignKey('Output',related_name="inner_input_rel",blank=True,null=True) #za subprocess
    PARAMETER_CHOICES = (
        ('text','Single line'),
        ('textarea','Multi line text'),
        ('select', 'Select box'),
Janez K's avatar
Janez K committed
1041
1042
        ('file', 'File field'),
        ('checkbox', 'Checkbox'),
Janez K's avatar
Janez K committed
1043
1044
    )
    parameter_type = models.CharField(max_length=50,choices=PARAMETER_CHOICES,blank=True,null=True)
1045
1046
1047
1048
    order = models.PositiveIntegerField(default=1)

    class Meta:
        ordering = ('order',)
Janez K's avatar
Janez K committed
1049

1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
    def import_from_json(self,json_data,input_conversion,output_conversion):
        self.name = json_data['name']
        self.short_name = json_data['short_name']
        self.description = json_data['description']
        self.variable = json_data['variable']
        self.required = json_data['required']
        self.parameter = json_data['parameter']
        self.multi_id = json_data['multi_id']
        self.parameter_type = json_data['parameter_type']
        self.order = json_data['order']
1060
1061
        if self.parameter:
            self.value = json_data['value']
1062
1063
        self.save()
        input_conversion[json_data['pk']]=self.pk
Janez K's avatar
Janez K committed
1064
        for option in json_data['options']:
1065
1066
            o = Option()
            o.input = self
Janez K's avatar
Janez K committed
1067
            o.import_from_json(option,input_conversion,output_conversion)
1068
1069
1070
1071
1072
1073
1074
            o.save()
        if json_data['outer_output']:
            self.outer_output = Output.objects.get(pk=output_conversion[json_data['outer_output']])
            self.outer_output.inner_input = self
            self.outer_output.save()
            self.save()

1075
1076
1077
1078
1079
1080
1081
1082
    def export(self):
        d = {}
        d['name']=self.name
        d['short_name']=self.short_name
        d['description']=self.description
        d['variable']=self.variable
        d['required']=self.required
        d['parameter']=self.parameter
1083
1084
1085
        d['value']=None
        if self.parameter:
            d['value']=self.value
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
        d['multi_id']=self.multi_id
        d['parameter_type']=self.parameter_type
        d['order']=self.order
        d['options']=[]
        d['pk']=self.pk
        for o in self.options.all():
            d['options'].append(o.export())
        try:
            d['inner_output']=self.inner_output.pk
        except:
            d['inner_output']=None
        try:
            d['outer_output']=self.outer_output.pk
        except:
            d['outer_output']=None
        return d

Janez K's avatar
Janez K committed
1103
    def __unicode__(self):
Janez K's avatar
Janez K committed
1104
1105
        return unicode(self.name)

dejan's avatar
dejan committed
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
"""class InputCrossValidation(models.Model):
    name = models.CharField(max_length=200)
    short_name = models.CharField(max_length=3)
    description = models.TextField(blank=True,null=True)
    variable = models.CharField(max_length=50)
    widget = models.ForeignKey(Widget,related_name="inputs2")
    required = models.BooleanField()
    parameter = models.BooleanField()
    value = PickledObjectField(null=True)
    multi_id = models.IntegerField(default=0)
    inner_output1 = models.ForeignKey('OutputCrossValidation',related_name="outer_input_rel",blank=True,null=True) #za subprocess
    #inner_output2 = models.ForeignKey('OutputCrossValidation',related_name="outer_input_rel",blank=True,null=True) #za subprocess
    outer_output = models.ForeignKey('OutputCrossValidation',related_name="inner_input_rel",blank=True,null=True) #za subprocess
    PARAMETER_CHOICES = (
        ('text','Single line'),
        ('textarea','Multi line text'),
        ('select', 'Select box'),
    )
    parameter_type = models.CharField(max_length=50,choices=PARAMETER_CHOICES,blank=True,null=True)
    order = models.PositiveIntegerField(default=1)

    class Meta:
        ordering = ('order',)

    def __unicode__(self):
        return unicode(self.name)

class OutputCrossValidation(models.Model):
    name = models.CharField(max_length=200)
    short_name = models.CharField(max_length=5)
    description = models.TextField(blank=True)
    variable = models.CharField(max_length=50)
    widget = models.ForeignKey(Widget,related_name="outputs")
    value = PickledObjectField(null=True)
    inner_input = models.ForeignKey(InputCrossValidation,related_name="outer_output_rel",blank=True,null=True) #za subprocess
    outer_input = models.ForeignKey(InputCrossValidation,related_name="inner_output_rel",blank=True,null=True) #za subprocess
    order = models.PositiveIntegerField(default=1)

    class Meta:
        ordering = ('order',)

    def __unicode__(self):
        return unicode(self.name)"""

Janez K's avatar
Janez K committed
1150
1151
1152
1153
class Option(models.Model):
    input = models.ForeignKey(Input,related_name="options")
    name = models.CharField(max_length=200)
    value = models.TextField(blank=True,null=True)
Janez K's avatar
Janez K committed
1154

1155
1156
1157
1158
1159
    def import_from_json(self,json_data,input_conversion,output_conversion):
        self.name = json_data['name']
        self.value = json_data['value']
        self.save()

1160
1161
1162
1163
1164
1165
    def export(self):
        d = {}
        d['name']=self.name
        d['value']=self.value
        return d

Janez K's avatar
Janez K committed
1166
    class Meta:
Janez K's avatar
Janez K committed
1167
        ordering = ['name']
Janez K's avatar
Janez K committed
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177

class Output(models.Model):
    name = models.CharField(max_length=200)
    short_name = models.CharField(max_length=5)
    description = models.TextField(blank=True)
    variable = models.CharField(max_length=50)
    widget = models.ForeignKey(Widget,related_name="outputs")
    value = PickledObjectField(null=True)
    inner_input = models.ForeignKey(Input,related_name="outer_output_rel",blank=True,null=True) #za subprocess
    outer_input = models.ForeignKey(Input,related_name="inner_output_rel",blank=True,null=True) #za subprocess
1178
1179
    order = models.PositiveIntegerField(default=1)

1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
    def import_from_json(self,json_data,input_conversion,output_conversion):
        self.name = json_data['name']
        self.short_name = json_data['short_name']
        self.description = json_data['description']
        self.variable = json_data['variable']
        self.order = json_data['order']
        self.save()
        output_conversion[json_data['pk']]=self.pk
        if json_data['outer_input']:
            self.outer_input = Input.objects.get(pk=input_conversion[json_data['outer_input']])
            self.outer_input.inner_output = self
            self.outer_input.save()        
            self.save()

1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212

    def export(self):
        d = {}
        d['name']=self.name
        d['short_name']=self.short_name
        d['description']=self.description
        d['variable']=self.variable
        d['order']=self.order
        d['pk']=self.pk
        try:
            d['inner_input']=self.inner_input.pk
        except:
            d['inner_input']=None
        try:
            d['outer_input']=self.outer_input.pk
        except:
            d['outer_input']=None
        return d

1213
1214
    class Meta:
        ordering = ('order',)
Janez K's avatar
Janez K committed
1215

Janez K's avatar
Janez K committed
1216
1217
1218
1219
1220
1221
    def __unicode__(self):
        return unicode(self.name)

class UserProfile(models.Model):
    user