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.
Files changed (68) hide show
  1. django_to_galaxy/__init__.py +1 -0
  2. django_to_galaxy/admin/__init__.py +13 -0
  3. django_to_galaxy/admin/galaxy_element.py +6 -0
  4. django_to_galaxy/admin/galaxy_instance.py +26 -0
  5. django_to_galaxy/admin/galaxy_output_file.py +79 -0
  6. django_to_galaxy/admin/galaxy_user.py +183 -0
  7. django_to_galaxy/admin/history.py +91 -0
  8. django_to_galaxy/admin/invocation.py +154 -0
  9. django_to_galaxy/admin/tag.py +17 -0
  10. django_to_galaxy/admin/utils.py +116 -0
  11. django_to_galaxy/admin/workflow.py +131 -0
  12. django_to_galaxy/api/__init__.py +0 -0
  13. django_to_galaxy/api/serializers/asymetricslugrelatedfield.py +31 -0
  14. django_to_galaxy/api/serializers/create_dataset_collection.py +118 -0
  15. django_to_galaxy/api/serializers/create_history.py +6 -0
  16. django_to_galaxy/api/serializers/galaxy_instance.py +10 -0
  17. django_to_galaxy/api/serializers/galaxy_output_file.py +17 -0
  18. django_to_galaxy/api/serializers/galaxy_user.py +20 -0
  19. django_to_galaxy/api/serializers/history.py +17 -0
  20. django_to_galaxy/api/serializers/invocation.py +22 -0
  21. django_to_galaxy/api/serializers/invoke_workflow.py +24 -0
  22. django_to_galaxy/api/serializers/upload_to_history.py +25 -0
  23. django_to_galaxy/api/serializers/workflow.py +41 -0
  24. django_to_galaxy/api/urls.py +53 -0
  25. django_to_galaxy/api/views/create_dataset_collection.py +461 -0
  26. django_to_galaxy/api/views/create_history.py +23 -0
  27. django_to_galaxy/api/views/galaxy_instance.py +13 -0
  28. django_to_galaxy/api/views/galaxy_output_file.py +15 -0
  29. django_to_galaxy/api/views/galaxy_user.py +13 -0
  30. django_to_galaxy/api/views/history.py +14 -0
  31. django_to_galaxy/api/views/invocation.py +45 -0
  32. django_to_galaxy/api/views/invoke_workflow.py +181 -0
  33. django_to_galaxy/api/views/upload_to_history.py +78 -0
  34. django_to_galaxy/api/views/workflow.py +14 -0
  35. django_to_galaxy/apps.py +6 -0
  36. django_to_galaxy/migrations/0001_new_initial.py +171 -0
  37. django_to_galaxy/migrations/0002_rename_state_history_galaxy_state_and_more.py +37 -0
  38. django_to_galaxy/migrations/0003_invocation_create_time.py +20 -0
  39. django_to_galaxy/migrations/0004_alter_galaxyuser_email_galaxyoutputfile.py +46 -0
  40. django_to_galaxy/migrations/0005_alter_galaxyoutputfile_invocation_and_more.py +37 -0
  41. django_to_galaxy/migrations/0006_tag_history_tags_workflow_tags.py +42 -0
  42. django_to_galaxy/migrations/0007_format_alter_history_tags_alter_workflow_tags_and_more.py +63 -0
  43. django_to_galaxy/migrations/0008_workflowinput_label.py +21 -0
  44. django_to_galaxy/migrations/0009_galaxyoutputfile_unique_galaxy_id_per_invocation.py +20 -0
  45. django_to_galaxy/migrations/0010_workflow__is_meta_workflow__step_jobs_count.py +23 -0
  46. django_to_galaxy/migrations/0011_rename__step_jobs_count_workflow__step_count_and_more.py +23 -0
  47. django_to_galaxy/migrations/0012_workflowinput_collection_type_and_more.py +136 -0
  48. django_to_galaxy/migrations/__init__.py +0 -0
  49. django_to_galaxy/models/__init__.py +8 -0
  50. django_to_galaxy/models/accepted_input.py +139 -0
  51. django_to_galaxy/models/galaxy_element.py +52 -0
  52. django_to_galaxy/models/galaxy_instance.py +29 -0
  53. django_to_galaxy/models/galaxy_output_file.py +53 -0
  54. django_to_galaxy/models/galaxy_user.py +94 -0
  55. django_to_galaxy/models/history.py +73 -0
  56. django_to_galaxy/models/invocation.py +251 -0
  57. django_to_galaxy/models/workflow.py +440 -0
  58. django_to_galaxy/schemas/__init__.py +0 -0
  59. django_to_galaxy/schemas/dataset.py +16 -0
  60. django_to_galaxy/settings.py +8 -0
  61. django_to_galaxy/templates/admin/import_workflows.html +89 -0
  62. django_to_galaxy/urls.py +3 -0
  63. django_to_galaxy/utils.py +61 -0
  64. django_to_galaxy/version.py +3 -0
  65. django_to_galaxy-0.7.0.0.dist-info/METADATA +17 -0
  66. django_to_galaxy-0.7.0.0.dist-info/RECORD +68 -0
  67. django_to_galaxy-0.7.0.0.dist-info/WHEEL +4 -0
  68. 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}]"