django-to-galaxy 0.6.9.7__py3-none-any.whl → 0.6.9.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of django-to-galaxy might be problematic. Click here for more details.
- django_to_galaxy/admin/__init__.py +7 -1
- django_to_galaxy/admin/galaxy_user.py +21 -35
- django_to_galaxy/admin/utils.py +112 -0
- django_to_galaxy/admin/workflow.py +61 -6
- django_to_galaxy/api/serializers/create_dataset_collection.py +118 -0
- django_to_galaxy/api/serializers/create_history.py +6 -0
- django_to_galaxy/api/serializers/invocation.py +5 -0
- django_to_galaxy/api/serializers/invoke_workflow.py +4 -0
- django_to_galaxy/api/serializers/workflow.py +6 -0
- django_to_galaxy/api/urls.py +15 -0
- django_to_galaxy/api/views/create_dataset_collection.py +461 -0
- django_to_galaxy/api/views/create_history.py +3 -0
- django_to_galaxy/api/views/invocation.py +6 -1
- django_to_galaxy/api/views/invoke_workflow.py +23 -21
- django_to_galaxy/migrations/0011_rename__step_jobs_count_workflow__step_count_and_more.py +23 -0
- django_to_galaxy/migrations/0012_workflowinput_collection_type_and_more.py +136 -0
- django_to_galaxy/models/__init__.py +1 -1
- django_to_galaxy/models/accepted_input.py +113 -2
- django_to_galaxy/models/galaxy_instance.py +1 -1
- django_to_galaxy/models/invocation.py +41 -12
- django_to_galaxy/models/workflow.py +366 -30
- django_to_galaxy/version.py +1 -1
- {django_to_galaxy-0.6.9.7.dist-info → django_to_galaxy-0.6.9.9.dist-info}/METADATA +4 -3
- {django_to_galaxy-0.6.9.7.dist-info → django_to_galaxy-0.6.9.9.dist-info}/RECORD +26 -20
- {django_to_galaxy-0.6.9.7.dist-info → django_to_galaxy-0.6.9.9.dist-info}/WHEEL +1 -1
- {django_to_galaxy-0.6.9.7.dist-info → django_to_galaxy-0.6.9.9.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Generated by Django 5.2.3 on 2025-09-09 13:15
|
|
2
|
+
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
from django.db import migrations, models
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
(
|
|
11
|
+
"django_to_galaxy",
|
|
12
|
+
"0011_rename__step_jobs_count_workflow__step_count_and_more",
|
|
13
|
+
),
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
operations = [
|
|
17
|
+
migrations.AddField(
|
|
18
|
+
model_name="workflowinput",
|
|
19
|
+
name="collection_type",
|
|
20
|
+
field=models.CharField(
|
|
21
|
+
blank=True,
|
|
22
|
+
choices=[
|
|
23
|
+
("record", "Record"),
|
|
24
|
+
("paired", "Dataset Pair"),
|
|
25
|
+
("list", "List of Datasets"),
|
|
26
|
+
("list:record", "List of Records"),
|
|
27
|
+
("list:paired", "List of Dataset Pairs"),
|
|
28
|
+
(
|
|
29
|
+
"list:paired_or_unpaired",
|
|
30
|
+
"Mixed List of Paired and Unpaired Datasets",
|
|
31
|
+
),
|
|
32
|
+
],
|
|
33
|
+
default=None,
|
|
34
|
+
max_length=25,
|
|
35
|
+
null=True,
|
|
36
|
+
),
|
|
37
|
+
),
|
|
38
|
+
migrations.AddField(
|
|
39
|
+
model_name="workflowinput",
|
|
40
|
+
name="default_value",
|
|
41
|
+
field=models.CharField(blank=True, max_length=255, null=True),
|
|
42
|
+
),
|
|
43
|
+
migrations.AddField(
|
|
44
|
+
model_name="workflowinput",
|
|
45
|
+
name="input_type",
|
|
46
|
+
field=models.CharField(
|
|
47
|
+
choices=[
|
|
48
|
+
("data_input", "data_input"),
|
|
49
|
+
("data_collection_input", "data_collection_input"),
|
|
50
|
+
("parameter_input", "parameter_input"),
|
|
51
|
+
],
|
|
52
|
+
default="data_input",
|
|
53
|
+
max_length=25,
|
|
54
|
+
),
|
|
55
|
+
),
|
|
56
|
+
migrations.AddField(
|
|
57
|
+
model_name="workflowinput",
|
|
58
|
+
name="multiple",
|
|
59
|
+
field=models.BooleanField(default=False),
|
|
60
|
+
),
|
|
61
|
+
migrations.AddField(
|
|
62
|
+
model_name="workflowinput",
|
|
63
|
+
name="parameter_type",
|
|
64
|
+
field=models.CharField(
|
|
65
|
+
blank=True,
|
|
66
|
+
choices=[
|
|
67
|
+
("text", "text"),
|
|
68
|
+
("integer", "integer"),
|
|
69
|
+
("float", "float"),
|
|
70
|
+
("boolean", "boolean"),
|
|
71
|
+
("color", "color"),
|
|
72
|
+
("directory_uri", "directory_uri"),
|
|
73
|
+
],
|
|
74
|
+
default=None,
|
|
75
|
+
max_length=15,
|
|
76
|
+
null=True,
|
|
77
|
+
),
|
|
78
|
+
),
|
|
79
|
+
migrations.CreateModel(
|
|
80
|
+
name="WorkflowInputTextOption",
|
|
81
|
+
fields=[
|
|
82
|
+
(
|
|
83
|
+
"id",
|
|
84
|
+
models.BigAutoField(
|
|
85
|
+
auto_created=True,
|
|
86
|
+
primary_key=True,
|
|
87
|
+
serialize=False,
|
|
88
|
+
verbose_name="ID",
|
|
89
|
+
),
|
|
90
|
+
),
|
|
91
|
+
("text_option", models.CharField(max_length=255)),
|
|
92
|
+
(
|
|
93
|
+
"workflow_input",
|
|
94
|
+
models.ForeignKey(
|
|
95
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
96
|
+
to="django_to_galaxy.workflowinput",
|
|
97
|
+
),
|
|
98
|
+
),
|
|
99
|
+
],
|
|
100
|
+
),
|
|
101
|
+
migrations.AddField(
|
|
102
|
+
model_name="workflowinput",
|
|
103
|
+
name="restrict_text_values",
|
|
104
|
+
field=models.ManyToManyField(to="django_to_galaxy.workflowinputtextoption"),
|
|
105
|
+
),
|
|
106
|
+
migrations.AddConstraint(
|
|
107
|
+
model_name="workflowinput",
|
|
108
|
+
constraint=models.CheckConstraint(
|
|
109
|
+
condition=models.Q(
|
|
110
|
+
models.Q(
|
|
111
|
+
("input_type", "parameter_input"),
|
|
112
|
+
("parameter_type__isnull", False),
|
|
113
|
+
("collection_type__isnull", True),
|
|
114
|
+
),
|
|
115
|
+
models.Q(
|
|
116
|
+
("input_type", "data_input"),
|
|
117
|
+
("parameter_type__isnull", True),
|
|
118
|
+
("collection_type__isnull", True),
|
|
119
|
+
),
|
|
120
|
+
models.Q(
|
|
121
|
+
("input_type", "data_collection_input"),
|
|
122
|
+
("parameter_type__isnull", True),
|
|
123
|
+
("collection_type__isnull", False),
|
|
124
|
+
),
|
|
125
|
+
_connector="OR",
|
|
126
|
+
),
|
|
127
|
+
name="workflowinputs_types_congruant",
|
|
128
|
+
),
|
|
129
|
+
),
|
|
130
|
+
migrations.AddConstraint(
|
|
131
|
+
model_name="workflowinputtextoption",
|
|
132
|
+
constraint=models.UniqueConstraint(
|
|
133
|
+
fields=("workflow_input", "text_option"), name="unique_input_option"
|
|
134
|
+
),
|
|
135
|
+
),
|
|
136
|
+
]
|
|
@@ -5,4 +5,4 @@ from .history import History # noqa
|
|
|
5
5
|
from .invocation import Invocation # noqa
|
|
6
6
|
from .workflow import Workflow # noqa
|
|
7
7
|
from .galaxy_element import Tag # noqa
|
|
8
|
-
from .accepted_input import WorkflowInput, Format # noqa
|
|
8
|
+
from .accepted_input import WorkflowInput, Format, WorkflowInputTextOption # noqa
|
|
@@ -1,28 +1,139 @@
|
|
|
1
1
|
from django.db import models
|
|
2
|
+
from django.db.models import Q
|
|
3
|
+
|
|
4
|
+
DATA = "data_input"
|
|
5
|
+
COLLECTION = "data_collection_input"
|
|
6
|
+
PARAMETER = "parameter_input"
|
|
7
|
+
INPUT_TYPE_CHOICES = [
|
|
8
|
+
(DATA, "data_input"),
|
|
9
|
+
(COLLECTION, "data_collection_input"),
|
|
10
|
+
(PARAMETER, "parameter_input"),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
P_TEXT = "text"
|
|
14
|
+
P_INTEGER = "integer"
|
|
15
|
+
P_FLOAT = "float"
|
|
16
|
+
P_BOOLEAN = "boolean"
|
|
17
|
+
P_COLOR = "color"
|
|
18
|
+
P_DIRECTORY_URI = "directory_uri"
|
|
19
|
+
PARAMETER_TYPE_CHOICES = [
|
|
20
|
+
(P_TEXT, "text"),
|
|
21
|
+
(P_INTEGER, "integer"),
|
|
22
|
+
(P_FLOAT, "float"),
|
|
23
|
+
(P_BOOLEAN, "boolean"),
|
|
24
|
+
(P_COLOR, "color"),
|
|
25
|
+
(P_DIRECTORY_URI, "directory_uri"),
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
C_RECORD = "record"
|
|
29
|
+
C_PAIRED = "paired"
|
|
30
|
+
C_LIST = "list"
|
|
31
|
+
C_LIST_RECORD = "list:record"
|
|
32
|
+
C_LIST_PAIRED = "list:paired"
|
|
33
|
+
C_LIST_PAIRED_UNPAIRED = "list:paired_or_unpaired"
|
|
34
|
+
COLLECTION_TYPE_CHOICES = [
|
|
35
|
+
(C_RECORD, "Record"),
|
|
36
|
+
(C_PAIRED, "Dataset Pair"),
|
|
37
|
+
(C_LIST, "List of Datasets"),
|
|
38
|
+
(C_LIST_RECORD, "List of Records"),
|
|
39
|
+
(C_LIST_PAIRED, "List of Dataset Pairs"),
|
|
40
|
+
(C_LIST_PAIRED_UNPAIRED, "Mixed List of Paired and Unpaired Datasets"),
|
|
41
|
+
]
|
|
2
42
|
|
|
3
43
|
|
|
4
44
|
class Format(models.Model):
|
|
5
|
-
format = models.CharField(max_length=200, unique=True)
|
|
6
45
|
"""Format on the galaxy side."""
|
|
7
46
|
|
|
47
|
+
format = models.CharField(max_length=200, unique=True)
|
|
48
|
+
|
|
8
49
|
def __str__(self):
|
|
9
50
|
return f"{self.format}"
|
|
10
51
|
|
|
11
52
|
|
|
53
|
+
class WorkflowInputTextOption(models.Model):
|
|
54
|
+
"""Text option for a workflow input on Galaxy side."""
|
|
55
|
+
|
|
56
|
+
workflow_input = models.ForeignKey(
|
|
57
|
+
"WorkflowInput", null=False, on_delete=models.CASCADE
|
|
58
|
+
)
|
|
59
|
+
text_option = models.CharField(max_length=255)
|
|
60
|
+
|
|
61
|
+
class Meta:
|
|
62
|
+
constraints = [
|
|
63
|
+
models.UniqueConstraint(
|
|
64
|
+
fields=["workflow_input", "text_option"], name="unique_input_option"
|
|
65
|
+
)
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
def __str__(self):
|
|
69
|
+
return f"{self.text_option}"
|
|
70
|
+
|
|
71
|
+
def __repr__(self):
|
|
72
|
+
return f"Input: {self!s}"
|
|
73
|
+
|
|
74
|
+
|
|
12
75
|
class WorkflowInput(models.Model):
|
|
76
|
+
"""Accepted input for a workflow on Galaxy side."""
|
|
77
|
+
|
|
13
78
|
galaxy_step_id = models.IntegerField(null=False)
|
|
14
79
|
"""Step id on the galaxy side."""
|
|
15
80
|
label = models.CharField(max_length=100, blank=True)
|
|
16
81
|
"""Label on the galaxy side."""
|
|
17
82
|
workflow = models.ForeignKey("Workflow", null=False, on_delete=models.CASCADE)
|
|
18
83
|
"""Workflow id."""
|
|
84
|
+
input_type = models.CharField(
|
|
85
|
+
max_length=25, choices=INPUT_TYPE_CHOICES, default=DATA
|
|
86
|
+
)
|
|
87
|
+
"""Type of input on the galaxy side."""
|
|
19
88
|
formats = models.ManyToManyField("Format")
|
|
20
89
|
"""Accepted input formats on the galaxy side."""
|
|
90
|
+
parameter_type = models.CharField(
|
|
91
|
+
max_length=15,
|
|
92
|
+
choices=PARAMETER_TYPE_CHOICES,
|
|
93
|
+
default=None,
|
|
94
|
+
null=True,
|
|
95
|
+
blank=True,
|
|
96
|
+
)
|
|
97
|
+
"""Type of input if it is a parameter input."""
|
|
98
|
+
collection_type = models.CharField(
|
|
99
|
+
max_length=25,
|
|
100
|
+
choices=COLLECTION_TYPE_CHOICES,
|
|
101
|
+
default=None,
|
|
102
|
+
null=True,
|
|
103
|
+
blank=True,
|
|
104
|
+
)
|
|
105
|
+
"""Type of input if it is a parameter input."""
|
|
21
106
|
optional = models.BooleanField(default=False)
|
|
22
107
|
"""Workflow input optional information on the galaxy side."""
|
|
108
|
+
default_value = models.CharField(max_length=255, null=True, blank=True)
|
|
109
|
+
"""Default value of the input (either text, integer, float, boolean)"""
|
|
110
|
+
multiple = models.BooleanField(default=False)
|
|
111
|
+
"""If the input can get multiple values on the galaxy side."""
|
|
112
|
+
|
|
113
|
+
class Meta:
|
|
114
|
+
constraints = [
|
|
115
|
+
models.CheckConstraint(
|
|
116
|
+
check=Q(
|
|
117
|
+
Q(input_type=PARAMETER)
|
|
118
|
+
& Q(parameter_type__isnull=False)
|
|
119
|
+
& Q(collection_type__isnull=True)
|
|
120
|
+
)
|
|
121
|
+
| Q(
|
|
122
|
+
Q(input_type=DATA)
|
|
123
|
+
& Q(parameter_type__isnull=True)
|
|
124
|
+
& Q(collection_type__isnull=True)
|
|
125
|
+
)
|
|
126
|
+
| Q(
|
|
127
|
+
Q(input_type=COLLECTION)
|
|
128
|
+
& Q(parameter_type__isnull=True)
|
|
129
|
+
& Q(collection_type__isnull=False)
|
|
130
|
+
),
|
|
131
|
+
name="workflowinputs_types_congruant",
|
|
132
|
+
)
|
|
133
|
+
]
|
|
23
134
|
|
|
24
135
|
def __str__(self):
|
|
25
|
-
return f"{self.label}
|
|
136
|
+
return f"{self.label}"
|
|
26
137
|
|
|
27
138
|
def __repr__(self):
|
|
28
139
|
return f"Input: {self!s}"
|
|
@@ -22,7 +22,7 @@ class GalaxyInstance(models.Model):
|
|
|
22
22
|
Whether the instance is available or not.
|
|
23
23
|
"""
|
|
24
24
|
try:
|
|
25
|
-
response = requests.
|
|
25
|
+
response = requests.head(self.url, timeout=30)
|
|
26
26
|
response.raise_for_status()
|
|
27
27
|
return True
|
|
28
28
|
except Exception:
|
|
@@ -57,7 +57,7 @@ class Invocation(models.Model):
|
|
|
57
57
|
step_job_summary["states"] = {}
|
|
58
58
|
step_job_summary["id"] = step_job.job_id
|
|
59
59
|
step_job_summary["states"][
|
|
60
|
-
self.
|
|
60
|
+
self.galaxy_invocation.gi.jobs.get(step_job.job_id).state
|
|
61
61
|
] = 1
|
|
62
62
|
step_jobs_summary.append(step_job_summary)
|
|
63
63
|
|
|
@@ -88,37 +88,66 @@ class Invocation(models.Model):
|
|
|
88
88
|
|
|
89
89
|
@property
|
|
90
90
|
def percentage_done(self) -> float:
|
|
91
|
-
"""Retrieve percentage of
|
|
91
|
+
"""Retrieve percentage of steps done for the invocation."""
|
|
92
92
|
if self.status == DONE:
|
|
93
93
|
return 100.0
|
|
94
94
|
self.step_jobs_summary = self.step_jobs_summary()
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
inv_steps = self.galaxy_invocation.steps
|
|
97
|
+
for step in inv_steps:
|
|
97
98
|
if "subworkflow_invocation_id" in step.wrapped:
|
|
98
99
|
if step.wrapped["subworkflow_invocation_id"]:
|
|
99
100
|
self.complet_jobs_summary(step)
|
|
100
101
|
|
|
101
|
-
|
|
102
|
+
count_job_states = defaultdict(int)
|
|
102
103
|
for step in self.step_jobs_summary:
|
|
103
104
|
for key in step["states"].keys():
|
|
104
|
-
|
|
105
|
+
count_job_states[key] += 1
|
|
106
|
+
count_scheduled_steps = sum(step.state == "scheduled" for step in inv_steps)
|
|
107
|
+
wf_step_count = self.workflow._step_count
|
|
105
108
|
try:
|
|
106
|
-
percentage_done =
|
|
107
|
-
count_states.get("ok", 0) / self.workflow.get_step_jobs_count()
|
|
108
|
-
)
|
|
109
|
+
percentage_done = count_scheduled_steps / wf_step_count
|
|
109
110
|
except ZeroDivisionError:
|
|
110
111
|
percentage_done = 0
|
|
111
112
|
if percentage_done == 1:
|
|
112
|
-
|
|
113
|
-
self.
|
|
114
|
-
|
|
113
|
+
last_job = inv_steps[wf_step_count - 1]
|
|
114
|
+
if self.check_if_last_job_is_ok(last_job):
|
|
115
|
+
self.status = DONE
|
|
116
|
+
self.save()
|
|
117
|
+
else:
|
|
118
|
+
try:
|
|
119
|
+
percentage_done = (count_scheduled_steps - 1) / wf_step_count
|
|
120
|
+
except ZeroDivisionError:
|
|
121
|
+
percentage_done = 0
|
|
122
|
+
if "error" in count_job_states.keys():
|
|
115
123
|
self.status = ERROR
|
|
116
124
|
self.save()
|
|
117
|
-
elif "paused" in
|
|
125
|
+
elif "paused" in count_job_states.keys():
|
|
118
126
|
self.status = PAUSED
|
|
119
127
|
self.save()
|
|
120
128
|
return percentage_done * 100
|
|
121
129
|
|
|
130
|
+
def check_if_last_job_is_ok(self, last_job):
|
|
131
|
+
"""Checks if the very last job is done"""
|
|
132
|
+
if last_job.job_id:
|
|
133
|
+
return self.galaxy_invocation.gi.jobs.get(last_job.job_id).state == "ok"
|
|
134
|
+
if (
|
|
135
|
+
"subworkflow_invocation_id" in last_job.wrapped
|
|
136
|
+
and last_job.wrapped["subworkflow_invocation_id"]
|
|
137
|
+
and len(
|
|
138
|
+
self.galaxy_invocation.gi.invocations.get(
|
|
139
|
+
last_job.wrapped["subworkflow_invocation_id"]
|
|
140
|
+
).steps
|
|
141
|
+
)
|
|
142
|
+
> 0
|
|
143
|
+
):
|
|
144
|
+
return self.check_if_last_job_is_ok(
|
|
145
|
+
self.galaxy_invocation.gi.invocations.get(
|
|
146
|
+
last_job.wrapped["subworkflow_invocation_id"]
|
|
147
|
+
).steps[-1]
|
|
148
|
+
)
|
|
149
|
+
return False
|
|
150
|
+
|
|
122
151
|
@property
|
|
123
152
|
def job_id_to_tools(self) -> Dict[str, dict]:
|
|
124
153
|
"""Dict of job_id to wrapped tool."""
|