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.

Files changed (26) hide show
  1. django_to_galaxy/admin/__init__.py +7 -1
  2. django_to_galaxy/admin/galaxy_user.py +21 -35
  3. django_to_galaxy/admin/utils.py +112 -0
  4. django_to_galaxy/admin/workflow.py +61 -6
  5. django_to_galaxy/api/serializers/create_dataset_collection.py +118 -0
  6. django_to_galaxy/api/serializers/create_history.py +6 -0
  7. django_to_galaxy/api/serializers/invocation.py +5 -0
  8. django_to_galaxy/api/serializers/invoke_workflow.py +4 -0
  9. django_to_galaxy/api/serializers/workflow.py +6 -0
  10. django_to_galaxy/api/urls.py +15 -0
  11. django_to_galaxy/api/views/create_dataset_collection.py +461 -0
  12. django_to_galaxy/api/views/create_history.py +3 -0
  13. django_to_galaxy/api/views/invocation.py +6 -1
  14. django_to_galaxy/api/views/invoke_workflow.py +23 -21
  15. django_to_galaxy/migrations/0011_rename__step_jobs_count_workflow__step_count_and_more.py +23 -0
  16. django_to_galaxy/migrations/0012_workflowinput_collection_type_and_more.py +136 -0
  17. django_to_galaxy/models/__init__.py +1 -1
  18. django_to_galaxy/models/accepted_input.py +113 -2
  19. django_to_galaxy/models/galaxy_instance.py +1 -1
  20. django_to_galaxy/models/invocation.py +41 -12
  21. django_to_galaxy/models/workflow.py +366 -30
  22. django_to_galaxy/version.py +1 -1
  23. {django_to_galaxy-0.6.9.7.dist-info → django_to_galaxy-0.6.9.9.dist-info}/METADATA +4 -3
  24. {django_to_galaxy-0.6.9.7.dist-info → django_to_galaxy-0.6.9.9.dist-info}/RECORD +26 -20
  25. {django_to_galaxy-0.6.9.7.dist-info → django_to_galaxy-0.6.9.9.dist-info}/WHEEL +1 -1
  26. {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} of {self.workflow!r}"
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.get(self.url, timeout=2)
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.workflow.galaxy_owner.obj_gi.jobs.get(step_job.job_id).state
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 jobs done for the invocation."""
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
- for step in self.galaxy_invocation.steps:
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
- count_states = defaultdict(int)
102
+ count_job_states = defaultdict(int)
102
103
  for step in self.step_jobs_summary:
103
104
  for key in step["states"].keys():
104
- count_states[key] += 1
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
- self.status = DONE
113
- self.save()
114
- if "error" in count_states.keys():
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 count_states.keys():
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."""