Skip to content

Commit c73e9bc

Browse files
committed
Refactor and tidy
1 parent d36604c commit c73e9bc

4 files changed

Lines changed: 84 additions & 112 deletions

File tree

weblab/experiments/models.py

Lines changed: 69 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,75 @@
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+
@property
45+
def entities(self):
46+
"""Entity objects related to this experiment"""
47+
return (self.model, self.protocol)
48+
49+
def is_visible_to_user(self, user):
50+
"""
51+
Can the user view the experiment?
52+
53+
:param user: user to test against
54+
55+
:returns: True if the user is allowed to view the experiment, False otherwise
56+
"""
57+
return visibility_check(self.visibility, self.viewers, user)
58+
59+
@property
60+
def viewers(self):
61+
"""
62+
Get users which have special permissions to view this experiment.
63+
64+
We take the intersection of users with special permissions to view each object
65+
(model, fitting spec, etc) involved, if that object is private. If it's public,
66+
we can ignore it because everyone can see it.
67+
68+
:return: `set` of `User` objects
69+
"""
70+
viewers = [
71+
obj.viewers
72+
for obj in self.entities
73+
if obj.visibility == Visibility.PRIVATE
74+
]
75+
return set.intersection(*viewers) if viewers else {}
76+
77+
class Meta:
78+
abstract = True
79+
80+
81+
class Experiment(ExperimentMixin, UserCreatedModelMixin, models.Model):
1482
"""A specific version of a protocol run on a specific version of a model
1583
1684
This class essentially just stores the model & protocol links. The results are
@@ -34,9 +102,6 @@ class Meta:
34102
('create_experiment', 'Can create experiments'),
35103
)
36104

37-
def __str__(self):
38-
return self.name
39-
40105
@property
41106
def name(self):
42107
return self.get_name()
@@ -60,55 +125,6 @@ def get_name(self, model_version=False, proto_version=False):
60125
def visibility(self):
61126
return get_joint_visibility(self.model_version.visibility, self.protocol_version.visibility)
62127

63-
@property
64-
def viewers(self):
65-
"""
66-
Get users which have special permissions to view this experiment
67-
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.
71-
72-
:return: `set` of `User` objects
73-
"""
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 ''
111-
112128

113129
class Runnable(UserCreatedModelMixin, FileCollectionMixin, models.Model):
114130
""" Runnable base class

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: 5 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
from django.db import models
22

33
from core.models import UserCreatedModelMixin
4-
from core.visibility import Visibility, get_joint_visibility, visibility_check
4+
from core.visibility import get_joint_visibility
55
from datasets.models import Dataset
66
from entities.models import (
77
Entity,
88
EntityManager,
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(ExperimentMixin, UserCreatedModelMixin, 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)