Skip to content

Commit 1b89fbd

Browse files
committed
Refactor and tidy
1 parent 1354dbb commit 1b89fbd

4 files changed

Lines changed: 72 additions & 103 deletions

File tree

weblab/experiments/models.py

Lines changed: 58 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,49 @@
1010
from repocache.models import CachedModelVersion, CachedProtocolVersion
1111

1212

13-
class Experiment(UserCreatedModelMixin, models.Model):
13+
class ExperimentMixin(models.Model):
14+
"""
15+
Model mixin for different types of experiment
16+
17+
Models must have model, model_version, protocol and protocol_version fields
18+
and be the parent of a Runnable-derived model.
19+
"""
20+
def __str__(self):
21+
return self.name
22+
23+
@property
24+
def latest_version(self):
25+
return self.versions.latest('created_at')
26+
27+
@property
28+
def nice_model_version(self):
29+
"""Use tags to give a nicer representation of the commit id"""
30+
return self.model_version.nice_version()
31+
32+
@property
33+
def nice_protocol_version(self):
34+
"""Use tags to give a nicer representation of the commit id"""
35+
return self.protocol_version.nice_version()
36+
37+
@property
38+
def latest_result(self):
39+
try:
40+
return self.latest_version.status
41+
except Runnable.DoesNotExist:
42+
return ''
43+
44+
def is_visible_to_user(self, user):
45+
"""
46+
Can the user view the experiment?
47+
48+
:param user: user to test against
49+
50+
:returns: True if the user is allowed to view the experiment, False otherwise
51+
"""
52+
return visibility_check(self.visibility, self.viewers, user)
53+
54+
55+
class Experiment(UserCreatedModelMixin, ExperimentMixin, models.Model):
1456
"""A specific version of a protocol run on a specific version of a model
1557
1658
This class essentially just stores the model & protocol links. The results are
@@ -34,9 +76,6 @@ class Meta:
3476
('create_experiment', 'Can create experiments'),
3577
)
3678

37-
def __str__(self):
38-
return self.name
39-
4079
@property
4180
def name(self):
4281
return self.get_name()
@@ -60,54 +99,28 @@ def get_name(self, model_version=False, proto_version=False):
6099
def visibility(self):
61100
return get_joint_visibility(self.model_version.visibility, self.protocol_version.visibility)
62101

102+
@property
103+
def entities(self):
104+
"""Entity objects related to this experiment"""
105+
return (self.model, self.protocol)
106+
63107
@property
64108
def viewers(self):
65109
"""
66-
Get users which have special permissions to view this experiment
110+
Get users which have special permissions to view this experiment.
67111
68-
We do not handle the case where both model and protocol are public,
69-
since this would make the experiment also public and therefore
70-
visible to every user - so calling this method makes very little sense.
112+
We take the intersection of users with special permissions to view each object
113+
(model, fitting spec, etc) involved, if that object is private. If it's public,
114+
we can ignore it because everyone can see it.
71115
72116
:return: `set` of `User` objects
73117
"""
74-
if self.protocol.visibility != Visibility.PRIVATE:
75-
return self.model.viewers
76-
elif self.model.visibility != Visibility.PRIVATE:
77-
return self.protocol.viewers
78-
else:
79-
return self.model.viewers & self.protocol.viewers
80-
81-
def is_visible_to_user(self, user):
82-
"""
83-
Can the user view the experiment?
84-
85-
:param user: user to test against
86-
87-
:returns: True if the user is allowed to view the experiment, False otherwise
88-
"""
89-
return visibility_check(self.visibility, self.viewers, user)
90-
91-
@property
92-
def latest_version(self):
93-
return self.versions.latest('created_at')
94-
95-
@property
96-
def nice_model_version(self):
97-
"""Use tags to give a nicer representation of the commit id"""
98-
return self.model_version.nice_version()
99-
100-
@property
101-
def nice_protocol_version(self):
102-
"""Use tags to give a nicer representation of the commit id"""
103-
return self.protocol_version.nice_version()
104-
105-
@property
106-
def latest_result(self):
107-
try:
108-
return self.latest_version.status
109-
except Runnable.DoesNotExist:
110-
return ''
118+
viewers = [
119+
obj.viewers
120+
for obj in self.entities
121+
if obj.visibility == Visibility.PRIVATE
122+
]
123+
return set.intersection(*viewers) if viewers else {}
111124

112125

113126
class Runnable(UserCreatedModelMixin, FileCollectionMixin, models.Model):

weblab/experiments/processing.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ class ProcessingException(Exception):
4545

4646

4747
def submit_runnable(runnable, body, user):
48+
"""Submit a Celery task to the Chaste backend
49+
50+
@param runnable Runnable object to submit
51+
@param body dict of extra parameters to post to the request
52+
@param user user making the request
53+
"""
4854
run = RunningExperiment.objects.create(runnable=runnable)
4955
signature = runnable.signature
5056

@@ -93,8 +99,6 @@ def submit_runnable(runnable, body, user):
9399

94100
runnable.save()
95101

96-
return runnable, True
97-
98102

99103
def submit_experiment(model, model_version, protocol, protocol_version, user, rerun_ok):
100104
"""Submit a Celery task to run an experiment.
@@ -144,11 +148,11 @@ def submit_experiment(model, model_version, protocol, protocol_version, user, re
144148
'model': urljoin(settings.CALLBACK_BASE_URL, model_url),
145149
'protocol': urljoin(settings.CALLBACK_BASE_URL, protocol_url),
146150
}
147-
148151
if protocol.is_fitting_spec:
149152
body['dataset'] = body['fittingSpec'] = body['protocol']
150153

151-
return submit_runnable(version, body, user)
154+
submit_runnable(version, body, user)
155+
return version, True
152156

153157

154158
def cancel_experiment(task_id):

weblab/fitting/models.py

Lines changed: 4 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
ModelEntity,
1010
ProtocolEntity,
1111
)
12-
from experiments.models import Runnable
12+
from experiments.models import ExperimentMixin, Runnable
1313
from repocache.models import CachedFittingSpecVersion, CachedModelVersion, CachedProtocolVersion
1414

1515

@@ -59,7 +59,7 @@ def collaborators(self):
5959
return self.entity_ptr.collaborators
6060

6161

62-
class FittingResult(UserCreatedModelMixin, models.Model):
62+
class FittingResult(UserCreatedModelMixin, ExperimentMixin, models.Model):
6363
"""Represents the result of running a parameter fitting experiment.
6464
6565
This class essentially just stores the links to (particular versions of) a fitting spec,
@@ -69,7 +69,6 @@ class FittingResult(UserCreatedModelMixin, models.Model):
6969
There will only ever be one FittingResult for a given combination of model, protocol,
7070
dataset and fitting spec versions.
7171
72-
TODO: Consider creating a mixin for fields/methods shared with Experiment.
7372
"""
7473
fittingspec = models.ForeignKey(FittingSpec, related_name='fitting_results')
7574
dataset = models.ForeignKey(Dataset, related_name='fitting_results')
@@ -91,9 +90,6 @@ class Meta:
9190
('run_fits', 'Can run parameter fitting experiments'),
9291
)
9392

94-
def __str__(self):
95-
return self.name
96-
9793
@property
9894
def name(self):
9995
"""There isn't an obvious easy naming for fitting results..."""
@@ -109,53 +105,8 @@ def visibility(self):
109105
)
110106

111107
@property
112-
def viewers(self):
113-
"""
114-
Get users which have special permissions to view this experiment.
115-
116-
We take the intersection of users with special permissions to view each object
117-
(model, fitting spec, etc) involved, if that object is private. If it's public,
118-
we can ignore it because everyone can see it.
119-
120-
:return: `set` of `User` objects
121-
"""
122-
viewers = [
123-
obj.viewers
124-
for obj in (self.fittingspec, self.dataset, self.model, self.protocol)
125-
if obj.visibility == Visibility.PRIVATE
126-
]
127-
return set.intersection(*viewers) if viewers else {}
128-
129-
def is_visible_to_user(self, user):
130-
"""
131-
Can the user view the experiment?
132-
133-
:param user: user to test against
134-
135-
:returns: True if the user is allowed to view the experiment, False otherwise
136-
"""
137-
return visibility_check(self.visibility, self.viewers, user)
138-
139-
@property
140-
def latest_version(self):
141-
return self.versions.latest('created_at')
142-
143-
@property
144-
def nice_model_version(self):
145-
"""Use tags to give a nicer representation of the commit id"""
146-
return self.model_version.nice_version()
147-
148-
@property
149-
def nice_protocol_version(self):
150-
"""Use tags to give a nicer representation of the commit id"""
151-
return self.protocol_version.nice_version()
152-
153-
@property
154-
def latest_result(self):
155-
try:
156-
return self.latest_version.status
157-
except Runnable.DoesNotExist:
158-
return ''
108+
def entities(self):
109+
return (self.fittingspec, self.dataset, self.model, self.protocol)
159110

160111

161112
class FittingResultVersion(Runnable):

weblab/fitting/processing.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,5 @@ def submit_fitting(
8080
'dataset': urljoin(settings.CALLBACK_BASE_URL, dataset_url),
8181
}
8282

83-
return submit_runnable(version, body, user)
83+
submit_runnable(version, body, user)
84+
return version, True

0 commit comments

Comments
 (0)