models.py 48.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

Janez K's avatar
Janez K committed
30
31
32
33
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
34

Janez K's avatar
Janez K committed
35
    workflow = models.ForeignKey('Workflow',null=True,blank=True,related_name="categories")
36
37
38

    order = models.PositiveIntegerField(default=1)

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

Janez K's avatar
Janez K committed
41
42
    class Meta:
        verbose_name_plural = "categories"
43
44
        ordering = ('order','name',)

Janez K's avatar
Janez K committed
45
46
47
48
49
50
51
    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
52
53
54
55
    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
56
57
    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
58

Janez K's avatar
Janez K committed
59
    def can_be_streaming(self):
dejan's avatar
dejan committed
60
61
        """ 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
62
63
64
65
        if self.widgets.filter(abstract_widget__is_streaming=True).count()>0:
            return True
        else:
            return False
Janez K's avatar
Janez K committed
66

Janez K's avatar
Janez K committed
67
    def is_for_loop(self):
dejan's avatar
dejan committed
68
69
        """ 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
70
71
72
        if self.widgets.filter(type='for_input').count()>0:
            return True
        else:
Janez K's avatar
Janez K committed
73
74
            return False

dejan's avatar
dejan committed
75
76
77
78
79
80
81
82
    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
83
    def get_ready_to_run(self):
dejan's avatar
dejan committed
84
        """ Method prepares this workflows widgets. Returns a list of widget id-s. """
Janez K's avatar
Janez K committed
85
86
87
88
        widgets = self.widgets.all()
        unfinished_list = []
        for w in widgets:
            if not w.finished and not w.running:
dejan's avatar
dejan committed
89
                """ if widget isn't finished and is not running than true"""
Janez K's avatar
Janez K committed
90
91
92
93
                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
94
                        """ if widget not finished than true """
Janez K's avatar
Janez K committed
95
96
97
98
99
                        ready_to_run = False
                        break
                if ready_to_run:
                    unfinished_list.append(w.id)
        return unfinished_list
Janez K's avatar
Janez K committed
100

Janez K's avatar
Janez K committed
101
    def get_runnable_widgets(self):
dejan's avatar
dejan committed
102
103
        """ 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
104
105
106
107
        widgets = self.widgets.all()
        unfinished_list = []
        for w in widgets:
            if not w.finished and not w.running:
dejan's avatar
dejan committed
108
                """ if widget isn't finished and is not running than true"""
Janez K's avatar
Janez K committed
109
110
111
112
113
114
115
116
117
118
119
                ready_to_run = True
                connections = self.connections.filter(input__widget=w)
                for c in connections:
                    if not c.output.widget.finished:
                        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
120
121
        """ 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
122
        #clear for_input and for_output
dejan's avatar
dejan committed
123
        print("run_for_loop")
Janez K's avatar
Janez K committed
124
125
126
127
128
        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
129

dejan's avatar
dejan committed
130
131
        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
132
133
        current_iteration = 0
        for i in input_list:
Anze Vavpetic's avatar
Anze Vavpetic committed
134
            #print(i);
dejan's avatar
dejan committed
135
136
137
138
            """ 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
139
140
            proper_output.value = i
            proper_output.save()
dejan's avatar
dejan committed
141
            fi.finished=True # set the input widget as finished
Janez K's avatar
Janez K committed
142
            fi.save()
Janez K's avatar
Janez K committed
143
            if not USE_CONCURRENCY or 1==1:
dejan's avatar
dejan committed
144
                """ This if statement is always true. """
Janez K's avatar
Janez K committed
145
146
147
148
                unfinished_list = self.get_runnable_widgets()
                try:
                    while len(unfinished_list)>0:
                        for w in unfinished_list:
dejan's avatar
dejan committed
149
                            w.run(True) # run the widget
Janez K's avatar
Janez K committed
150
                            total = self.widgets.count()
Janez K's avatar
Janez K committed
151
                            completed = self.widgets.filter(finished=True).count()
Janez K's avatar
Janez K committed
152
153
154
155
156
157
                            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
158
                """ This part is never executed  """
Janez K's avatar
Janez K committed
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
                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
188
                                        self.widget.save()
Janez K's avatar
Janez K committed
189
190
191
192
193
                            unfinished_list = self.get_runnable_widgets()
                except:
                    raise
            current_iteration = current_iteration+1

dejan's avatar
dejan committed
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
    def run_cross_validation(self):
        """ Method runs cross_validation. """
        #clear for_input and for_output
        print("run_cross_validation")
        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:
220
            input_seed = random.randint(0, 10**9)
dejan's avatar
dejan committed
221

222
223
224
225
226
227
228
229
230
231
        # 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
232
233
234
235
        progress_total = len(input_list) # for progress bar
        current_iteration = 0

        # create folds
236
237
238

        folds = []
        if hasattr(input_list, "get_items_ref"):
239
            import orange 
240
            # Orange table on input, so we cannot do slices
241
            indices = orange.MakeRandomIndicesCV(input_list, randseed=input_seed, folds=input_fold, stratified=orange.MakeRandomIndices.Stratified)
242
243
244
            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
245
		#print len(output_train), len(output_test)
246
                folds.append((output_train, output_test))
247
        else:
248
249
            rand.seed(input_seed)
            rand.shuffle(input_list)
250
            folds = [input_list[i::input_fold] for i in range(input_fold)]
dejan's avatar
dejan committed
251
252
253
254
255
256
257
258
259
260

        # 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:
261
262
                    i.value = []
                    i.save()
dejan's avatar
dejan committed
263
264

        for i in range(len(folds)):
265
266
            #import pdb; pdb.set_trace()
            if hasattr(input_list, "get_items_ref"):
Marko Lalovic's avatar
Marko Lalovic committed
267
268
                output_test = folds[i][1]
                output_train = folds[i][0]
269
270
271
            else:
                output_train = folds[:i] + folds[i+1:]
                output_test = folds[i]
272
273
274
275
276
277
278
279
            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
280
281
282
283
            """ 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
284
            proper_output.value = output_train
dejan's avatar
dejan committed
285
286
            proper_output.save()
            proper_output = fi.outputs.all()[1] # inner output
287
            proper_output.value = output_test
dejan's avatar
dejan committed
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
            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
307
    def run(self):
Janez K's avatar
Janez K committed
308
        if not USE_CONCURRENCY or not self.widget:
Janez K's avatar
Janez K committed
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
            unfinished_list = self.get_runnable_widgets()
            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()
            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
342
                    unfinished_list = self.get_runnable_widgets()
Janez K's avatar
Janez K committed
343
344
345
346
347
348
349
350
351
352
353
                    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
354
                                    self.widget.save()
Janez K's avatar
Janez K committed
355
                        time.sleep(1)
Janez K's avatar
Janez K committed
356
                    unfinished_list = self.get_runnable_widgets()
Janez K's avatar
Janez K committed
357
358
359
360
            except:
                raise
    def rename(self,new_name):
        self.name = new_name
Janez K's avatar
Janez K committed
361
362
        self.save()

Janez K's avatar
Janez K committed
363
364
    @models.permalink
    def get_absolute_url(self):
Janez K's avatar
Janez K committed
365
366
        return ('open workflow', [str(self.id)])

Janez K's avatar
Janez K committed
367
368
369
    @models.permalink
    def get_copy_url(self):
        return ('copy workflow', [str(self.id)])
Janez K's avatar
Janez K committed
370

Janez K's avatar
Janez K committed
371
372
373
    @models.permalink
    def get_info_url(self):
        return ('workflow information', [str(self.id)])
Janez K's avatar
Janez K committed
374

Janez K's avatar
Janez K committed
375
376
    def __unicode__(self):
        return unicode(self.name)
Janez K's avatar
Janez K committed
377

Janez K's avatar
Janez K committed
378
    class Meta:
Janez K's avatar
Janez K committed
379
        ordering = ['name']
Janez K's avatar
Janez K committed
380
381

class AbstractWidget(models.Model):
Janez K's avatar
Janez K committed
382
383
    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
384
    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
385
    wsdl_method = models.CharField(max_length=200,blank=True,default='')
Janez K's avatar
Janez K committed
386
387
388
    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.')
389
    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
390
391
    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
392
393
    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
394

Janez K's avatar
Janez K committed
395
    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
396
    treeview_image = ThumbnailField(blank=True,null=True,upload_to="treeview",size=(16,16))
397

Janez K's avatar
Janez K committed
398
    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
399

Janez K's avatar
Janez K committed
400
401
    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
402

Janez K's avatar
Janez K committed
403
    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
404

Janez K's avatar
Janez K committed
405
    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
406

Janez K's avatar
Janez K committed
407
    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
408

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

Janez K's avatar
Janez K committed
411
412
    class Meta:
        ordering = ('order','name',)
413

Janez K's avatar
Janez K committed
414
415
416
417
418
419
420
421
422
    def set_uid(self,commit=False):
        import uuid
        self.uid = uuid.uuid4()
        if commit:
            self.save()
        for i in self.inputs.all():
            i.uid = uuid.uuid4()
            if commit:
                i.save()
bogdan's avatar
bogdan committed
423
            for option in i.options.all():
424
425
426
                option.uid = uuid.uuid4()
                if commit:
                    option.save()
Janez K's avatar
Janez K committed
427
428
429
430
431
        for o in self.outputs.all():
            o.uid = uuid.uuid4()
            if commit:
                o.save()

Janez K's avatar
Janez K committed
432
433
434
435
436
437
438
    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
439
    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
440
441
442
    widget = models.ForeignKey(AbstractWidget,related_name="inputs")
    required = models.BooleanField()
    parameter = models.BooleanField()
Janez K's avatar
Janez K committed
443
    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
444
445
446
    default = models.TextField(blank=True)
    PARAMETER_CHOICES = (
        ('text','Single line'),
447
        ('password', 'Password'),
Janez K's avatar
Janez K committed
448
449
450
451
452
453
        ('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
454

Janez K's avatar
Janez K committed
455
    order = models.PositiveIntegerField(default=1)
Janez K's avatar
Janez K committed
456
457

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

Janez K's avatar
Janez K committed
459
460
    def __unicode__(self):
        return unicode(self.name)
Janez K's avatar
Janez K committed
461

Janez K's avatar
Janez K committed
462
463
    class Meta:
        ordering = ('order',)
Janez K's avatar
Janez K committed
464

Janez K's avatar
Janez K committed
465
466
467
468
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
469
470
471

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

Janez K's avatar
Janez K committed
472
473
    def __unicode__(self):
        return unicode(self.name)
Janez K's avatar
Janez K committed
474

Janez K's avatar
Janez K committed
475
    class Meta:
Janez K's avatar
Janez K committed
476
        ordering = ['name']
Janez K's avatar
Janez K committed
477
478
479
480
481

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
482
    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
483
    widget = models.ForeignKey(AbstractWidget,related_name="outputs")
Janez K's avatar
Janez K committed
484

Janez K's avatar
Janez K committed
485
    order = models.PositiveIntegerField(default=1)
Janez K's avatar
Janez K committed
486
487

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

Janez K's avatar
Janez K committed
489
    class Meta:
Janez K's avatar
Janez K committed
490
491
        ordering = ('order',)

Janez K's avatar
Janez K committed
492
493
494
495
    def __unicode__(self):
        return unicode(self.name)

class Widget(models.Model):
dejan's avatar
dejan committed
496
497
    """ Widget """
    # django relationship (ForeignKey), each widget is related to a single workflow
Janez K's avatar
Janez K committed
498
    workflow = models.ForeignKey(Workflow,related_name="widgets")
dejan's avatar
dejan committed
499
500
501
    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
502
    abstract_widget = models.ForeignKey(AbstractWidget,related_name="instances",blank=True,null=True)
dejan's avatar
dejan committed
503
504
505
506
507
    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
508
509
510
511
512
513
514
    WIDGET_CHOICES = (
        ('regular','Regular widget'),
        ('subprocess','Subprocess widget'),
        ('input', 'Input widget'),
        ('output', 'Output widget'),
    )
    type = models.CharField(max_length=50,choices=WIDGET_CHOICES,default='regular')
Janez K's avatar
Janez K committed
515

Janez K's avatar
Janez K committed
516
    progress = models.IntegerField(default=0)
Janez K's avatar
Janez K committed
517

Janez K's avatar
Janez K committed
518
519
520
521
522
523
    def is_visualization(self):
        try:
            if self.abstract_widget.visualization_view != '':
                return True
        except:
            return False
Janez K's avatar
Janez K committed
524

Janez K's avatar
Janez K committed
525
526
527
528
529
530
    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
531

Janez K's avatar
Janez K committed
532
    def unfinish(self):
Janez K's avatar
Janez K committed
533
        self.reset_descendants()
Janez K's avatar
Janez K committed
534

Janez K's avatar
Janez K committed
535
536
537
538
539
540
541
542
    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
543

Janez K's avatar
Janez K committed
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
    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
562
            inp.outer_output.save()
Janez K's avatar
Janez K committed
563
564
565
566
567
568
        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
569

Janez K's avatar
Janez K committed
570
    def run(self,offline):
dejan's avatar
dejan committed
571
        """ This is only a hack, to make this work on windows """
Janez K's avatar
bugfix    
Janez K committed
572
        try: 
573
            if self.abstract_widget.windows_queue and settings.USE_WINDOWS_QUEUE:
Janez K's avatar
bugfix    
Janez K committed
574
575
576
577
578
                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
579
580
581
            self.proper_run(offline)

    def proper_run(self,offline):
dejan's avatar
dejan committed
582
583
        """ This is the real start. """
        print("proper_run_widget")
Janez K's avatar
Janez K committed
584
        if not self.ready_to_run():
Janez K's avatar
Janez K committed
585
            raise WidgetException("The prerequisites for running this widget have not been met.")
Janez K's avatar
Janez K committed
586
587
588
        self.running=True
        self.save()
        if self.type == 'regular' or self.type == 'subprocess':
dejan's avatar
dejan committed
589
            """ if this is a subprocess or a regular widget than true."""
Janez K's avatar
Janez K committed
590
            if not self.abstract_widget is None:
591
                """if this is an abstract widget than true we save the widget function in a variable """
Janez K's avatar
Janez K committed
592
593
594
595
                function_to_call = getattr(workflows.library,self.abstract_widget.action)
            input_dict = {}
            outputs = {}
            for i in self.inputs.all():
dejan's avatar
dejan committed
596
                """ we walk through all the inputs """
Janez K's avatar
Janez K committed
597
598
                #gremo pogledat ce obstaja povezava in ce obstaja gremo value prebrat iz outputa
                if not i.parameter:
dejan's avatar
dejan committed
599
                    """ if there is a connection than true and read the output value """
Janez K's avatar
Janez K committed
600
601
602
603
604
605
606
607
608
609
610
611
612
                    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
613
            start = time.time()
Janez K's avatar
Janez K committed
614
615
            try:
                if not self.abstract_widget is None:
dejan's avatar
dejan committed
616
617
                    """ 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
618
                    if self.abstract_widget.wsdl != '':
dejan's avatar
dejan committed
619
                        """ if abstrac widget is a web service """
Janez K's avatar
Janez K committed
620
621
622
                        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
623
                        """ if abstrac widget has a progress bar """
Janez K's avatar
fix    
Janez K committed
624
                        outputs = function_to_call(input_dict,self)
Janez K's avatar
Janez K committed
625
                    elif self.abstract_widget.is_streaming:
dejan's avatar
dejan committed
626
                        """ if abstrac widget is a stream """
Janez K's avatar
fix    
Janez K committed
627
                        outputs = function_to_call(input_dict,self,None)
Janez K's avatar
Janez K committed
628
                    else:
dejan's avatar
dejan committed
629
                        """ else run abstract widget function """
Janez K's avatar
fix    
Janez K committed
630
                        outputs = function_to_call(input_dict)
Janez K's avatar
Janez K committed
631
632
                else:
                    if self.workflow_link.is_for_loop():
dejan's avatar
dejan committed
633
634
635
                        """ if this is object is a for loop than true and run;
                        else false and run workflow """
                        print("proper_run_is_for_loop")
Janez K's avatar
Janez K committed
636
                        self.workflow_link.run_for_loop()
637
                        #print self.outputs.all()[0].value
dejan's avatar
dejan committed
638
639
                    elif self.workflow_link.is_cross_validation():
                        self.workflow_link.run_cross_validation()
Janez K's avatar
Janez K committed
640
641
642
643
644
645
646
647
                    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
648
649
            elapsed = (time.time()-start)
            outputs['clowdflows_elapsed']=elapsed
Janez K's avatar
Janez K committed
650
            for o in self.outputs.all():
dejan's avatar
dejan committed
651
                """ we walk through all the outputs """
Janez K's avatar
Janez K committed
652
                if not self.abstract_widget is None:
dejan's avatar
dejan committed
653
654
                    """ 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
655
656
657
658
                    try:
                        o.value = outputs[o.variable]
                    except:
                        pass
Janez K's avatar
Janez K committed
659
660
661
662
663
664
665
                    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
666
                """ if object is widget than true and configure parameters """
Janez K's avatar
Janez K committed
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
                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
682
683
            """ 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
684
685
            for o in self.outputs.all():
                o.value=o.outer_input.value
dejan's avatar
dejan committed
686
                #print(o.outer_input.value)
Janez K's avatar
Janez K committed
687
688
689
690
691
692
                o.save()
            self.finished=True
            self.running=False
            self.error=False
            self.save()
        elif self.type == 'for_output':
dejan's avatar
dejan committed
693
694
695
            """ 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
696
697
            for i in self.inputs.all():
                if not i.parameter:
dejan's avatar
dejan committed
698
                    """ if there is a connection than true and read the output value """
Janez K's avatar
Janez K committed
699
700
701
702
703
704
705
706
707
708
                    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
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
        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
740
        elif self.type == 'input':
dejan's avatar
dejan committed
741
            """ if object is an input widget for for loop than read all input values and finish """
Janez K's avatar
Janez K committed
742
743
744
745
746
747
748
749
            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
750
751
            """ if object is an output widget, then read output values and 
            configure parameters"""
Janez K's avatar
Janez K committed
752
753
            for i in self.inputs.all():
                if not i.parameter:
dejan's avatar
dejan committed
754
                    """ if there is a connection than true and read the output value """
Janez K's avatar
Janez K committed
755
756
757
758
759
760
                    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
761
            self.finished=True
Janez K's avatar
Janez K committed
762
763
764
            self.running=False
            self.error=False
        return None
Janez K's avatar
Janez K committed
765

766
    def reset(self,offline):
Janez K's avatar
Janez K committed
767
768
769
770
771
772
773
        #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()
774
775
776
777
        self.finished = False
        self.error = False
        self.running = False
        self.save()
Janez K's avatar
Janez K committed
778
779
        if self.type == 'subprocess':
            self.subunfinish()
780

Janez K's avatar
Janez K committed
781
    def reset_descendants(self):
dejan's avatar
dejan committed
782
        """ Method resets all the widget connections/descendants. """
Janez K's avatar
Janez K committed
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
        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
811
        #find all descendants and reset them as well
Janez K's avatar
Janez K committed
812
        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
813
814
815
816
817
818
819
820
821
822
823
824
825
        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
826
        Widget.objects.filter(id__in=widgets_that_need_reset).update(finished=False,error=False,running=False)
Janez K's avatar
Janez K committed
827
        for w in widgets_that_need_reset:
Janez K's avatar
Janez K committed
828
829
830
            if widgets_dict[w].type == 'subprocess':
                widgets_dict[w].subunfinish()
        #    widgets_dict[w].reset(False)
Janez K's avatar
Janez K committed
831
832
        return widgets_that_need_reset

Janez K's avatar
Janez K committed
833
834
    def run_post(self,request):
        if not self.ready_to_run():
Janez K's avatar
Janez K committed
835
            raise WidgetException("The prerequisites for running this widget have not been met.")
Janez K's avatar
Janez K committed
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
        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:
862
                if self.abstract_widget.windows_queue and settings.USE_WINDOWS_QUEUE:
Janez K's avatar
Janez K committed
863
                    t = executeWidgetPostInteract.apply_async([self,input_dict,output_dict,request],queue="windows")
864
865
                    outputs = t.wait()
                else:
Janez K's avatar
Janez K committed
866
                    outputs = executeWidgetPostInteract(self,input_dict,output_dict,request)
Janez K's avatar
Janez K committed
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
            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
886
887
        return outputs

Janez K's avatar
Janez K committed
888
889
    def __unicode__(self):
        return unicode(self.name)
Janez K's avatar
Janez K committed
890

Janez K's avatar
Janez K committed
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
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
907
908
        ('file', 'File field'),
        ('checkbox', 'Checkbox'),
Janez K's avatar
Janez K committed
909
910
    )
    parameter_type = models.CharField(max_length=50,choices=PARAMETER_CHOICES,blank=True,null=True)
911
912
913
914
    order = models.PositiveIntegerField(default=1)

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

Janez K's avatar
Janez K committed
916
    def __unicode__(self):
Janez K's avatar
Janez K committed
917
918
        return unicode(self.name)

dejan's avatar
dejan committed
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
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
"""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
963
964
965
966
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
967

Janez K's avatar
Janez K committed
968
    class Meta:
Janez K's avatar
Janez K committed
969
        ordering = ['name']
Janez K's avatar
Janez K committed
970
971
972
973
974
975
976
977
978
979

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
980
981
982
983
    order = models.PositiveIntegerField(default=1)

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

Janez K's avatar
Janez K committed
985
986
987
988
989
990
    def __unicode__(self):
        return unicode(self.name)

class UserProfile(models.Model):
    user = models.OneToOneField(User,related_name="userprofile")
    active_workflow = models.ForeignKey(Workflow,related_name="users",null=True,blank=True,on_delete=models.SET_NULL)
Janez K's avatar
Janez K committed
991

Janez K's avatar
Janez K committed
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
    def __unicode__(self):
        return unicode(self.user)

def create_user_profile(sender, instance, created, **kwargs):
    profile_set = UserProfile.objects.filter(user__id = instance.id)
    if created and not profile_set.exists():
        UserProfile.objects.create(user=instance)

# nardi da k nardimo userja da se avtomatsko nardi se UserProfile
post_save.connect(create_user_profile, sender=User)

def copy_workflow(old, user, parent_widget_conversion={},parent_input_conversion={},parent_output_conversion={},parent_widget=None):
    w = Workflow()
    if parent_widget is None:
        w.name = old.name+" (copy)"
    else:
        w.name = old.name
    w.user = user
    w.public = False
    w.description = old.description
    w.template_parent = old
    if not parent_widget is None:
        w.widget = parent_widget
    w.save()
    widget_conversion = {}
    input_conversion = {}
    output_conversion = {}
    for widget in old.widgets.all():
        new_widget = Widget()
        new_widget.workflow = w
        new_widget.x = widget.x
        new_widget.y = widget.y
        new_widget.name = widget.name
        new_widget.abstract_widget = widget.abstract_widget
        new_widget.finished = widget.finished
        new_widget.error = widget.error
        new_widget.running = widget.running
        new_widget.interaction_waiting = widget.interaction_waiting
        new_widget.type = widget.type
        new_widget.progress = widget.progress
        new_widget.save()
        widget_conversion[widget.id]=new_widget.id
        for input in widget.inputs.all():
            new_input = Input()
            new_input.name = input.name
            new_input.short_name = input.short_name
            new_input.description = input.description
            new_input.variable = input.variable
            new_input.widget = new_widget
            new_input.required = input.required
            new_input.parameter = input.parameter
            new_input.value = input.value
            new_input.multi_id = input.multi_id
            #inner_output nikol ne nastavlamo
            #outer_output in njemu spremenimo inner input
            if not parent_widget is None:
                if not input.outer_output is None:
                    new_input.outer_output = Output.objects.get(pk=parent_output_conversion[input.outer_output.id])
            new_input.parameter_type = input.parameter_type
            new_input.save()
            for option in input.options.all():
                new_option = Option()
                new_option.input = new_input
                new_option.name = option.name
                new_option.value = option.value
                new_option.save()
            if not parent_widget is None:
                if not input.outer_output is None:
                    new_input.outer_output.inner_input = new_input
                    new_input.outer_output.save()
            input_conversion[input.id]=new_input.id
        for output in widget.outputs.all():
            new_output = Output()
            new_output.name = output.name
            new_output.short_name = output.short_name
            new_output.description = output.description
            new_output.variable = output.variable
            new_output.widget = new_widget
            new_output.value = output.value
            #inner input nikol ne nastavlamo
            #outer input in njemu spremenimo inner output
            if not parent_widget is None:
                if not output.outer_input is None:
                    new_output.outer_input = Input.objects.get(pk=parent_input_conversion[output.outer_input.id])
            new_output.save()
            if not parent_widget is None:
                if not output.outer_input is None:
                    new_output.outer_input.inner_output = new_output
                    new_output.outer_input.save()
            output_conversion[output.id]=new_output.id
    for connection in old.connections.all():
        new_connection = Connection()
        new_connection.workflow = w
        new_connection.output = Output.objects.get(pk=output_conversion[connection.output.id])
        new_connection.input = Input.objects.get(pk=input_conversion[connection.input.id])
        new_connection.save()
    for widget in old.widgets.filter(type='subprocess'):
        #tuki mormo vse subprocesse zrihtat
        copy_workflow(widget.workflow_link, user, widget_conversion, input_conversion, output_conversion, Widget.objects.get(pk=widget_conversion[widget.id]))
Janez K's avatar
Janez K committed
1091
    return w