django-to-galaxy 0.7.0.0__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.
- django_to_galaxy/__init__.py +1 -0
- django_to_galaxy/admin/__init__.py +13 -0
- django_to_galaxy/admin/galaxy_element.py +6 -0
- django_to_galaxy/admin/galaxy_instance.py +26 -0
- django_to_galaxy/admin/galaxy_output_file.py +79 -0
- django_to_galaxy/admin/galaxy_user.py +183 -0
- django_to_galaxy/admin/history.py +91 -0
- django_to_galaxy/admin/invocation.py +154 -0
- django_to_galaxy/admin/tag.py +17 -0
- django_to_galaxy/admin/utils.py +116 -0
- django_to_galaxy/admin/workflow.py +131 -0
- django_to_galaxy/api/__init__.py +0 -0
- django_to_galaxy/api/serializers/asymetricslugrelatedfield.py +31 -0
- 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/galaxy_instance.py +10 -0
- django_to_galaxy/api/serializers/galaxy_output_file.py +17 -0
- django_to_galaxy/api/serializers/galaxy_user.py +20 -0
- django_to_galaxy/api/serializers/history.py +17 -0
- django_to_galaxy/api/serializers/invocation.py +22 -0
- django_to_galaxy/api/serializers/invoke_workflow.py +24 -0
- django_to_galaxy/api/serializers/upload_to_history.py +25 -0
- django_to_galaxy/api/serializers/workflow.py +41 -0
- django_to_galaxy/api/urls.py +53 -0
- django_to_galaxy/api/views/create_dataset_collection.py +461 -0
- django_to_galaxy/api/views/create_history.py +23 -0
- django_to_galaxy/api/views/galaxy_instance.py +13 -0
- django_to_galaxy/api/views/galaxy_output_file.py +15 -0
- django_to_galaxy/api/views/galaxy_user.py +13 -0
- django_to_galaxy/api/views/history.py +14 -0
- django_to_galaxy/api/views/invocation.py +45 -0
- django_to_galaxy/api/views/invoke_workflow.py +181 -0
- django_to_galaxy/api/views/upload_to_history.py +78 -0
- django_to_galaxy/api/views/workflow.py +14 -0
- django_to_galaxy/apps.py +6 -0
- django_to_galaxy/migrations/0001_new_initial.py +171 -0
- django_to_galaxy/migrations/0002_rename_state_history_galaxy_state_and_more.py +37 -0
- django_to_galaxy/migrations/0003_invocation_create_time.py +20 -0
- django_to_galaxy/migrations/0004_alter_galaxyuser_email_galaxyoutputfile.py +46 -0
- django_to_galaxy/migrations/0005_alter_galaxyoutputfile_invocation_and_more.py +37 -0
- django_to_galaxy/migrations/0006_tag_history_tags_workflow_tags.py +42 -0
- django_to_galaxy/migrations/0007_format_alter_history_tags_alter_workflow_tags_and_more.py +63 -0
- django_to_galaxy/migrations/0008_workflowinput_label.py +21 -0
- django_to_galaxy/migrations/0009_galaxyoutputfile_unique_galaxy_id_per_invocation.py +20 -0
- django_to_galaxy/migrations/0010_workflow__is_meta_workflow__step_jobs_count.py +23 -0
- 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/migrations/__init__.py +0 -0
- django_to_galaxy/models/__init__.py +8 -0
- django_to_galaxy/models/accepted_input.py +139 -0
- django_to_galaxy/models/galaxy_element.py +52 -0
- django_to_galaxy/models/galaxy_instance.py +29 -0
- django_to_galaxy/models/galaxy_output_file.py +53 -0
- django_to_galaxy/models/galaxy_user.py +94 -0
- django_to_galaxy/models/history.py +73 -0
- django_to_galaxy/models/invocation.py +251 -0
- django_to_galaxy/models/workflow.py +440 -0
- django_to_galaxy/schemas/__init__.py +0 -0
- django_to_galaxy/schemas/dataset.py +16 -0
- django_to_galaxy/settings.py +8 -0
- django_to_galaxy/templates/admin/import_workflows.html +89 -0
- django_to_galaxy/urls.py +3 -0
- django_to_galaxy/utils.py +61 -0
- django_to_galaxy/version.py +3 -0
- django_to_galaxy-0.7.0.0.dist-info/METADATA +17 -0
- django_to_galaxy-0.7.0.0.dist-info/RECORD +68 -0
- django_to_galaxy-0.7.0.0.dist-info/WHEEL +4 -0
- django_to_galaxy-0.7.0.0.dist-info/licenses/LICENSE +661 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from bioblend.galaxy.objects import wrappers
|
|
4
|
+
from django.db import models
|
|
5
|
+
|
|
6
|
+
from django_to_galaxy.schemas.dataset import SimpleDataset
|
|
7
|
+
from .galaxy_element import GalaxyElement
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class History(GalaxyElement):
|
|
11
|
+
"""Table for Galaxy history."""
|
|
12
|
+
|
|
13
|
+
galaxy_state = models.CharField(max_length=100)
|
|
14
|
+
"""State on the galaxy side."""
|
|
15
|
+
galaxy_owner = models.ForeignKey(
|
|
16
|
+
"GalaxyUser", null=False, on_delete=models.CASCADE, related_name="histories"
|
|
17
|
+
)
|
|
18
|
+
"""Galaxy user that owns the workflow."""
|
|
19
|
+
create_time = models.DateTimeField()
|
|
20
|
+
"""Time the invocation was created."""
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def galaxy_history(self) -> wrappers.History:
|
|
24
|
+
"""Galaxy object using bioblend."""
|
|
25
|
+
if getattr(self, "_galaxy_history", None) is None:
|
|
26
|
+
self._galaxy_history = self._get_galaxy_history()
|
|
27
|
+
return self._galaxy_history
|
|
28
|
+
|
|
29
|
+
def _get_galaxy_history(self) -> wrappers.History:
|
|
30
|
+
"""Get galaxy object using bioblend."""
|
|
31
|
+
return self.galaxy_owner.obj_gi.histories.get(self.galaxy_id)
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def simplify_datasets(self) -> List[SimpleDataset]:
|
|
35
|
+
"""Simplified version of datasets from history."""
|
|
36
|
+
if getattr(self, "_simplify_datasets", None) is None:
|
|
37
|
+
self._simplify_datasets = self._get_simplified_datasets()
|
|
38
|
+
return self._simplify_datasets
|
|
39
|
+
|
|
40
|
+
def _get_simplified_datasets(self) -> List[SimpleDataset]:
|
|
41
|
+
"""Get simplified version of datasets from history."""
|
|
42
|
+
return [
|
|
43
|
+
SimpleDataset(**dataset.wrapped)
|
|
44
|
+
for dataset in self.galaxy_history.get_datasets()
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
def delete(self, **kwargs):
|
|
48
|
+
"""Overloaded method to also delete history on Galaxy side."""
|
|
49
|
+
self.galaxy_owner.obj_gi.histories.delete(id_=self.galaxy_id, purge=True)
|
|
50
|
+
return super().delete(**kwargs)
|
|
51
|
+
|
|
52
|
+
def synchronize(self):
|
|
53
|
+
"""Synchronize data from Galaxy instance."""
|
|
54
|
+
galaxy_history = self._get_galaxy_history()
|
|
55
|
+
self.name = galaxy_history.name
|
|
56
|
+
self.published = galaxy_history.published
|
|
57
|
+
self.galaxy_state = galaxy_history.state
|
|
58
|
+
if self.galaxy_history.annotation is not None:
|
|
59
|
+
self.annotation = galaxy_history.annotation
|
|
60
|
+
self.save()
|
|
61
|
+
|
|
62
|
+
def upload_file(
|
|
63
|
+
self, file_path: str, **kwargs
|
|
64
|
+
) -> wrappers.HistoryDatasetAssociation:
|
|
65
|
+
"""Upload file to history."""
|
|
66
|
+
return self.galaxy_history.upload_file(file_path, **kwargs)
|
|
67
|
+
|
|
68
|
+
def __repr__(self):
|
|
69
|
+
return f"History: {super().__str__()}"
|
|
70
|
+
|
|
71
|
+
class Meta:
|
|
72
|
+
verbose_name_plural = "Histories"
|
|
73
|
+
unique_together = ("galaxy_id", "galaxy_owner")
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from collections import defaultdict
|
|
3
|
+
from time import sleep
|
|
4
|
+
from typing import Any, List, Dict
|
|
5
|
+
|
|
6
|
+
from bioblend.galaxy.objects import wrappers
|
|
7
|
+
from django.db import models
|
|
8
|
+
|
|
9
|
+
from .galaxy_output_file import GalaxyOutputFile
|
|
10
|
+
|
|
11
|
+
from django_to_galaxy.utils import enabled_cache
|
|
12
|
+
|
|
13
|
+
from django.core.exceptions import ObjectDoesNotExist
|
|
14
|
+
|
|
15
|
+
RUNNING = "running"
|
|
16
|
+
DONE = "done"
|
|
17
|
+
ERROR = "error"
|
|
18
|
+
PAUSED = "paused"
|
|
19
|
+
STATUS_CHOICES = [
|
|
20
|
+
(RUNNING, "Running"),
|
|
21
|
+
(PAUSED, "Paused"),
|
|
22
|
+
(ERROR, "Error"),
|
|
23
|
+
(DONE, "Done"),
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Invocation(models.Model):
|
|
30
|
+
"""Table for invocations of workflow."""
|
|
31
|
+
|
|
32
|
+
# Galaxy fields
|
|
33
|
+
galaxy_id = models.CharField(null=False, max_length=50)
|
|
34
|
+
"""Invocation id used on the galaxy side."""
|
|
35
|
+
galaxy_state = models.CharField(null=False, max_length=200)
|
|
36
|
+
"""State on the galaxy side."""
|
|
37
|
+
# Django fields
|
|
38
|
+
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default=RUNNING)
|
|
39
|
+
"""Status of the invocation."""
|
|
40
|
+
workflow = models.ForeignKey(
|
|
41
|
+
"Workflow", null=False, on_delete=models.CASCADE, related_name="workflows"
|
|
42
|
+
)
|
|
43
|
+
"""Workflow the invocation comes from."""
|
|
44
|
+
history = models.ForeignKey(
|
|
45
|
+
"History", null=False, on_delete=models.CASCADE, related_name="histories"
|
|
46
|
+
)
|
|
47
|
+
"""History used for the invocation."""
|
|
48
|
+
create_time = models.DateTimeField()
|
|
49
|
+
"""Time the invocation was created."""
|
|
50
|
+
|
|
51
|
+
def step_jobs_summary(self) -> List[Dict[str, Any]]:
|
|
52
|
+
"""Workaround self.galaxy_invocation.step_jobs_summary() due to v24.0 db issue"""
|
|
53
|
+
step_jobs_summary = []
|
|
54
|
+
for step_job in self.galaxy_invocation.steps:
|
|
55
|
+
if step_job.job_id is not None:
|
|
56
|
+
step_job_summary = {}
|
|
57
|
+
step_job_summary["states"] = {}
|
|
58
|
+
step_job_summary["id"] = step_job.job_id
|
|
59
|
+
step_job_summary["states"][
|
|
60
|
+
self.galaxy_invocation.gi.jobs.get(step_job.job_id).state
|
|
61
|
+
] = 1
|
|
62
|
+
step_jobs_summary.append(step_job_summary)
|
|
63
|
+
|
|
64
|
+
return step_jobs_summary
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def galaxy_invocation(self) -> wrappers.Invocation:
|
|
68
|
+
"""Galaxy object using bioblend."""
|
|
69
|
+
if getattr(self, "_galaxy_invocation", None) is None:
|
|
70
|
+
self._galaxy_invocation = self._get_galaxy_invocation()
|
|
71
|
+
return self._galaxy_invocation
|
|
72
|
+
|
|
73
|
+
def _get_galaxy_invocation(self) -> wrappers.Invocation:
|
|
74
|
+
"""Get galaxy object using bioblend."""
|
|
75
|
+
return self.workflow.galaxy_owner.obj_gi.invocations.get(self.galaxy_id)
|
|
76
|
+
|
|
77
|
+
def complet_jobs_summary(self, step):
|
|
78
|
+
subinv = self.galaxy_invocation.gi.invocations.get(
|
|
79
|
+
step.wrapped["subworkflow_invocation_id"]
|
|
80
|
+
)
|
|
81
|
+
sub_jobs_summary = subinv.step_jobs_summary()
|
|
82
|
+
for job in sub_jobs_summary:
|
|
83
|
+
self.step_jobs_summary.append(job)
|
|
84
|
+
for sub in subinv.steps:
|
|
85
|
+
if "subworkflow_invocation_id" in sub.wrapped:
|
|
86
|
+
if sub.wrapped["subworkflow_invocation_id"]:
|
|
87
|
+
self.complet_jobs_summary(sub)
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def percentage_done(self) -> float:
|
|
91
|
+
"""Retrieve percentage of steps done for the invocation."""
|
|
92
|
+
if self.status == DONE:
|
|
93
|
+
return 100.0
|
|
94
|
+
self.step_jobs_summary = self.step_jobs_summary()
|
|
95
|
+
|
|
96
|
+
inv_steps = self.galaxy_invocation.steps
|
|
97
|
+
for step in inv_steps:
|
|
98
|
+
if "subworkflow_invocation_id" in step.wrapped:
|
|
99
|
+
if step.wrapped["subworkflow_invocation_id"]:
|
|
100
|
+
self.complet_jobs_summary(step)
|
|
101
|
+
|
|
102
|
+
count_job_states = defaultdict(int)
|
|
103
|
+
for step in self.step_jobs_summary:
|
|
104
|
+
for key in step["states"].keys():
|
|
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
|
|
108
|
+
try:
|
|
109
|
+
percentage_done = count_scheduled_steps / wf_step_count
|
|
110
|
+
except ZeroDivisionError:
|
|
111
|
+
percentage_done = 0
|
|
112
|
+
if percentage_done == 1:
|
|
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():
|
|
123
|
+
self.status = ERROR
|
|
124
|
+
self.save()
|
|
125
|
+
elif "paused" in count_job_states.keys():
|
|
126
|
+
self.status = PAUSED
|
|
127
|
+
self.save()
|
|
128
|
+
return percentage_done * 100
|
|
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
|
+
|
|
151
|
+
@property
|
|
152
|
+
def job_id_to_tools(self) -> Dict[str, dict]:
|
|
153
|
+
"""Dict of job_id to wrapped tool."""
|
|
154
|
+
if getattr(self, "_job_id_to_tools", None) is None:
|
|
155
|
+
self._job_id_to_tools = self._build_job_id_to_tools()
|
|
156
|
+
return self._job_id_to_tools
|
|
157
|
+
|
|
158
|
+
def _build_job_id_to_tools(self) -> Dict[str, dict]:
|
|
159
|
+
step_jobs_summary = self.step_jobs_summary()
|
|
160
|
+
job_id_to_tools = {}
|
|
161
|
+
for step in step_jobs_summary:
|
|
162
|
+
job_id = step["id"]
|
|
163
|
+
job = self.workflow.galaxy_owner.obj_gi.jobs.get(job_id)
|
|
164
|
+
with enabled_cache():
|
|
165
|
+
wrapped_tool = self.workflow.galaxy_owner.obj_gi.tools.get(
|
|
166
|
+
job.wrapped["tool_id"]
|
|
167
|
+
).wrapped
|
|
168
|
+
job_id_to_tools[job_id] = wrapped_tool
|
|
169
|
+
return job_id_to_tools
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def detailed_step_jobs_summary(self) -> List[dict]:
|
|
173
|
+
"""Retrive `step_jobs_summary` with details of tool used."""
|
|
174
|
+
step_jobs_summary = self.step_jobs_summary()
|
|
175
|
+
detailed_jobs_summary = []
|
|
176
|
+
for step in step_jobs_summary:
|
|
177
|
+
detailed_step = step
|
|
178
|
+
job_id = step["id"]
|
|
179
|
+
detailed_step["tool"] = self.job_id_to_tools.get(job_id, {})
|
|
180
|
+
detailed_jobs_summary.append(detailed_step)
|
|
181
|
+
return detailed_jobs_summary
|
|
182
|
+
|
|
183
|
+
def synchronize(self):
|
|
184
|
+
"""Synchronize data from Galaxy instance."""
|
|
185
|
+
galaxy_invocation = self._get_galaxy_invocation()
|
|
186
|
+
self.galaxy_state = galaxy_invocation.state
|
|
187
|
+
self.save()
|
|
188
|
+
|
|
189
|
+
def create_output_files(self, max_retry: int = 3):
|
|
190
|
+
"""
|
|
191
|
+
Create output files generated in the invocation.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
max_retry: maximum number of time to try to retrieve info.
|
|
195
|
+
"""
|
|
196
|
+
galaxy_inv_wrapped = self._get_galaxy_invocation().wrapped
|
|
197
|
+
number_of_try = 1
|
|
198
|
+
while not galaxy_inv_wrapped.get("outputs", {}) and (number_of_try < max_retry):
|
|
199
|
+
sleep(0.25)
|
|
200
|
+
galaxy_inv_wrapped = self._get_galaxy_invocation().wrapped
|
|
201
|
+
number_of_try += 1
|
|
202
|
+
if galaxy_inv_wrapped.get("outputs", {}):
|
|
203
|
+
for output_name, output_content in galaxy_inv_wrapped["outputs"].items():
|
|
204
|
+
try:
|
|
205
|
+
output_file = GalaxyOutputFile.objects.get(
|
|
206
|
+
galaxy_id=output_content["id"],
|
|
207
|
+
invocation=self,
|
|
208
|
+
)
|
|
209
|
+
except ObjectDoesNotExist:
|
|
210
|
+
output_file = GalaxyOutputFile(
|
|
211
|
+
galaxy_id=output_content["id"],
|
|
212
|
+
workflow_name=output_name,
|
|
213
|
+
src=output_content["src"],
|
|
214
|
+
invocation=self,
|
|
215
|
+
)
|
|
216
|
+
output_file.save()
|
|
217
|
+
output_file.synchronize()
|
|
218
|
+
else:
|
|
219
|
+
logger.warning(
|
|
220
|
+
f"Could not create outputs from invocation: {self.galaxy_id}."
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
def update_output_files(self):
|
|
224
|
+
"""Update output files generated in the invocation."""
|
|
225
|
+
galaxy_inv_wrapped = self._get_galaxy_invocation().wrapped
|
|
226
|
+
if galaxy_inv_wrapped.get("outputs", {}):
|
|
227
|
+
for output_name, output_content in galaxy_inv_wrapped["outputs"].items():
|
|
228
|
+
try:
|
|
229
|
+
output_file = GalaxyOutputFile.objects.get(
|
|
230
|
+
galaxy_id=output_content["id"],
|
|
231
|
+
invocation=self,
|
|
232
|
+
)
|
|
233
|
+
except ObjectDoesNotExist:
|
|
234
|
+
output_file = GalaxyOutputFile(
|
|
235
|
+
galaxy_id=output_content["id"],
|
|
236
|
+
workflow_name=output_name,
|
|
237
|
+
src=output_content["src"],
|
|
238
|
+
invocation=self,
|
|
239
|
+
)
|
|
240
|
+
output_file.save()
|
|
241
|
+
output_file.synchronize()
|
|
242
|
+
else:
|
|
243
|
+
logger.warning(
|
|
244
|
+
f"Could not update outputs from invocation: {self.galaxy_id}."
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
def __repr__(self):
|
|
248
|
+
return f"Invocation: {self.__str__()}"
|
|
249
|
+
|
|
250
|
+
def __str__(self):
|
|
251
|
+
return f"{self.galaxy_id} [{self.workflow.name}]"
|