Skip to content

Commit a28a57b

Browse files
committed
Add visibility and naming tests for FittingResult
1 parent 0d4dc31 commit a28a57b

4 files changed

Lines changed: 223 additions & 19 deletions

File tree

weblab/conftest.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ def add_version(entity,
4242
populate_entity_cache(entity)
4343
return commit
4444

45+
@staticmethod
46+
def cached_version(entity, **kwargs):
47+
"""Add a single commit/version to an entity and return the relevant repocache entry"""
48+
assert kwargs.get('cache', True), "Cache must be true for cached version"
49+
version = Helpers.add_version(entity, **kwargs)
50+
return entity.repocache.get_version(version.sha)
51+
4552
@staticmethod
4653
def add_fake_version(entity, visibility='private', date=None, message='cache-only commit'):
4754
"""Add a new commit/version only in the cache, not in git."""
@@ -154,6 +161,19 @@ def public_protocol(helpers):
154161
return protocol
155162

156163

164+
@pytest.fixture
165+
def public_fittingspec(helpers):
166+
fittingspec = recipes.fittingspec.make()
167+
helpers.add_version(fittingspec, visibility='public')
168+
return fittingspec
169+
170+
171+
@pytest.fixture
172+
def public_dataset(helpers):
173+
dataset = recipes.dataset.make(visibility='public')
174+
return dataset
175+
176+
157177
@pytest.fixture
158178
def moderated_model(helpers):
159179
model = recipes.model.make()
@@ -331,3 +351,17 @@ def my_dataset_with_file(logged_in_user, helpers, public_protocol, client):
331351
)
332352
yield dataset
333353
dataset.delete()
354+
355+
356+
@pytest.fixture
357+
def fittingresult_version(public_model, public_protocol, public_fittingspec, public_dataset):
358+
return recipes.fittingresult_version.make(
359+
status='SUCCESS',
360+
fittingresult__model=public_model,
361+
fittingresult__model_version=public_model.repocache.latest_version,
362+
fittingresult__protocol=public_protocol,
363+
fittingresult__protocol_version=public_protocol.repocache.latest_version,
364+
fittingresult__fittingspec=public_fittingspec,
365+
fittingresult__fittingspec_version=public_fittingspec.repocache.latest_version,
366+
fittingresult__dataset=public_dataset,
367+
)

weblab/core/recipes.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
cached_protocol_version = Recipe('CachedProtocolVersion')
3131
cached_protocol_tag = Recipe('CachedProtocolTag')
3232

33+
cached_fittingspec = Recipe('CachedFittingSpec')
34+
cached_fittingspec_version = Recipe('CachedFittingSpecVersion')
35+
cached_fittingspec_tag = Recipe('CachedFittingSpecTag')
36+
3337
experiment = Recipe(
3438
'Experiment',
3539
model=foreign_key(model),
@@ -51,3 +55,16 @@
5155

5256
dataset_file = Recipe('DatasetFile',
5357
dataset=foreign_key(dataset))
58+
59+
fittingresult = Recipe(
60+
'FittingResult',
61+
model=foreign_key(model),
62+
model_version=foreign_key(cached_model_version),
63+
protocol=foreign_key(protocol),
64+
protocol_version=foreign_key(cached_protocol_version),
65+
fittingspec=foreign_key(fittingspec),
66+
fittingspec_version=foreign_key(cached_fittingspec_version),
67+
dataset=foreign_key(dataset),
68+
)
69+
70+
fittingresult_version = Recipe('FittingResultVersion', fittingresult=foreign_key(fittingresult))

weblab/fitting/models.py

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
ProtocolEntity,
1111
)
1212
from experiments.models import Runnable
13+
from repocache.models import CachedModelVersion, CachedProtocolVersion, CachedFittingSpecVersion
1314

1415

1516
class FittingSpec(Entity):
@@ -75,13 +76,9 @@ class FittingResult(UserCreatedModelMixin, models.Model):
7576
model = models.ForeignKey(ModelEntity, related_name='model_fitting_results')
7677
protocol = models.ForeignKey(ProtocolEntity, related_name='protocol_fitting_results')
7778

78-
# Note that we can't use a ForeignKey here, because versions of models, protocols & fitting
79-
# specs are not stored in the DB - they are just commits in the associated git repo.
80-
# We could link to the repocache tables, but those aren't guaranteed to exist or be permanent.
81-
# So instead we store the full git SHA as a string.
82-
fittingspec_version = models.CharField(max_length=50)
83-
model_version = models.CharField(max_length=50)
84-
protocol_version = models.CharField(max_length=50)
79+
model_version = models.ForeignKey(CachedModelVersion, default=None, null=False, related_name='model_ver_fitres')
80+
protocol_version = models.ForeignKey(CachedProtocolVersion, default=None, null=False, related_name='pro_ver_fitres')
81+
fittingspec_version = models.ForeignKey(CachedFittingSpecVersion, default=None, null=False, related_name='fit_ver_fitres')
8582

8683
class Meta:
8784
unique_together = ('fittingspec', 'dataset', 'model', 'protocol',
@@ -102,16 +99,10 @@ def name(self):
10299
@property
103100
def visibility(self):
104101
return get_joint_visibility(
105-
self.fittingspec.get_version_visibility(
106-
self.fittingspec_version,
107-
default=self.fittingspec.DEFAULT_VISIBILITY),
102+
self.fittingspec_version.visibility,
108103
self.dataset.visibility,
109-
self.model.get_version_visibility(
110-
self.model_version,
111-
default=self.model.DEFAULT_VISIBILITY),
112-
self.protocol.get_version_visibility(
113-
self.protocol_version,
114-
default=self.protocol.DEFAULT_VISIBILITY),
104+
self.model_version.visibility,
105+
self.protocol_version.visibility,
115106
)
116107

117108
@property
@@ -148,18 +139,18 @@ def latest_version(self):
148139
@property
149140
def nice_model_version(self):
150141
"""Use tags to give a nicer representation of the commit id"""
151-
return self.model.nice_version(self.model_version)
142+
return self.model_version.nice_version()
152143

153144
@property
154145
def nice_protocol_version(self):
155146
"""Use tags to give a nicer representation of the commit id"""
156-
return self.protocol.nice_version(self.protocol_version)
147+
return self.protocol_version.nice_version()
157148

158149
@property
159150
def latest_result(self):
160151
try:
161152
return self.latest_version.status
162-
except FittingResultVersion.DoesNotExist:
153+
except Runnable.DoesNotExist:
163154
return ''
164155

165156

weblab/fitting/tests/test_models.py

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
from datetime import date
2+
13
import pytest
24
from django.db.utils import IntegrityError
35
from django.shortcuts import get_object_or_404
46
from guardian.shortcuts import assign_perm
57

68
from core import recipes
9+
from datasets.models import Dataset
710
from repocache.models import CachedFittingSpec
11+
from repocache.populate import populate_entity_cache
812

913

1014
@pytest.mark.django_db
@@ -86,3 +90,161 @@ def test_get_repocache(self):
8690
assert CachedFittingSpec.objects.count() == 1
8791
spec.delete()
8892
assert CachedFittingSpec.objects.count() == 0
93+
94+
95+
@pytest.mark.django_db
96+
class TestFittingResult:
97+
def test_name(self, helpers):
98+
model = recipes.model.make(name='my model')
99+
protocol = recipes.protocol.make(name='my protocol')
100+
dataset = recipes.dataset.make(name='my dataset')
101+
fittingspec = recipes.fittingspec.make(name='my fitting spec')
102+
103+
model_version = helpers.add_version(model, tag_name='v1')
104+
protocol_version = helpers.add_version(protocol, tag_name='v2')
105+
fittingspec_version = helpers.add_version(fittingspec, tag_name='v3')
106+
107+
fitres = recipes.fittingresult.make(
108+
model=model,
109+
model_version=model.repocache.get_version(model_version.sha),
110+
protocol=protocol,
111+
protocol_version=protocol.repocache.get_version(protocol_version.sha),
112+
fittingspec=fittingspec,
113+
fittingspec_version=fittingspec.repocache.get_version(fittingspec_version.sha),
114+
dataset=dataset,
115+
)
116+
117+
assert str(fitres) == fitres.name == 'Fit my model to my dataset using my fitting spec'
118+
119+
def test_latest_version(self):
120+
v1 = recipes.fittingresult_version.make(created_at=date(2017, 1, 2))
121+
v2 = recipes.fittingresult_version.make(fittingresult=v1.fittingresult, created_at=date(2017, 1, 3))
122+
123+
assert v1.fittingresult.latest_version == v2
124+
assert not v1.is_latest
125+
assert v2.is_latest
126+
127+
def test_latest_result(self):
128+
ver = recipes.fittingresult_version.make(created_at=date(2017, 1, 2), status='FAILED')
129+
130+
assert ver.fittingresult.latest_result == 'FAILED'
131+
132+
def test_latest_result_empty_if_no_versions(self):
133+
fitres = recipes.fittingresult.make()
134+
135+
assert fitres.latest_result == ''
136+
137+
def test_nice_versions(self, fittingresult_version):
138+
fitres = fittingresult_version.fittingresult
139+
140+
assert fitres.nice_model_version == fitres.model.repocache.latest_version.sha[:8] + '...'
141+
assert fitres.nice_protocol_version == fitres.protocol.repocache.latest_version.sha[:8] + '...'
142+
143+
fitres.model.repo.tag('v1')
144+
populate_entity_cache(fitres.model)
145+
assert fitres.nice_model_version == 'v1'
146+
147+
fitres.protocol.repo.tag('v2')
148+
populate_entity_cache(fitres.protocol)
149+
150+
assert fitres.nice_protocol_version == 'v2'
151+
152+
def test_visibility(self, helpers):
153+
model = recipes.model.make()
154+
protocol = recipes.protocol.make()
155+
ds1 = recipes.dataset.make(visibility='private')
156+
ds2 = recipes.dataset.make(visibility='public')
157+
fittingspec = recipes.fittingspec.make()
158+
159+
mv1 = helpers.cached_version(model, visibility='private')
160+
mv2 = helpers.cached_version(model, visibility='public')
161+
pv1 = helpers.cached_version(protocol, visibility='private')
162+
pv2 = helpers.cached_version(protocol, visibility='public')
163+
fv1 = helpers.cached_version(fittingspec, visibility='private')
164+
fv2 = helpers.cached_version(fittingspec, visibility='public')
165+
166+
# all public
167+
assert recipes.fittingresult.make(
168+
model=model, model_version=mv2,
169+
protocol=protocol, protocol_version=pv2,
170+
fittingspec=fittingspec, fittingspec_version=fv2,
171+
dataset=ds2,
172+
).visibility == 'public'
173+
174+
# all private
175+
assert recipes.fittingresult.make(
176+
model=model, model_version=mv1,
177+
protocol=protocol, protocol_version=pv1,
178+
fittingspec=fittingspec, fittingspec_version=fv1,
179+
dataset=ds1,
180+
).visibility == 'private'
181+
182+
# model private version => private
183+
assert recipes.fittingresult.make(
184+
model=model, model_version=mv1,
185+
protocol=protocol, protocol_version=pv2,
186+
fittingspec=fittingspec, fittingspec_version=fv2,
187+
dataset=ds2,
188+
).visibility == 'private'
189+
190+
# protocol private version => private
191+
assert recipes.fittingresult.make(
192+
model=model, model_version=mv2,
193+
protocol=protocol, protocol_version=pv1,
194+
fittingspec=fittingspec, fittingspec_version=fv2,
195+
dataset=ds2,
196+
).visibility == 'private'
197+
198+
# fitting spec private version => private
199+
assert recipes.fittingresult.make(
200+
model=model, model_version=mv2,
201+
protocol=protocol, protocol_version=pv2,
202+
fittingspec=fittingspec, fittingspec_version=fv1,
203+
dataset=ds2,
204+
).visibility == 'private'
205+
206+
# dataset private version => private
207+
assert recipes.fittingresult.make(
208+
model=model, model_version=mv2,
209+
protocol=protocol, protocol_version=pv2,
210+
fittingspec=fittingspec, fittingspec_version=fv2,
211+
dataset=ds1,
212+
).visibility == 'private'
213+
214+
def test_viewers(self, helpers, user):
215+
helpers.add_permission(user, 'create_model')
216+
helpers.add_permission(user, 'create_protocol')
217+
helpers.add_permission(user, 'create_fittingspec')
218+
helpers.add_permission(user, 'create_dataset', model=Dataset)
219+
220+
model = recipes.model.make()
221+
protocol = recipes.protocol.make()
222+
fittingspec = recipes.fittingspec.make()
223+
# Datasets do not currently support collaborators
224+
# (https://github.com/ModellingWebLab/WebLab/issues/247)
225+
# so test with a public dataset for now
226+
dataset = recipes.dataset.make(visibility='public')
227+
mv = helpers.cached_version(model, visibility='private')
228+
pv = helpers.cached_version(protocol, visibility='private')
229+
fv = helpers.cached_version(fittingspec, visibility='private')
230+
231+
fr = recipes.fittingresult.make(
232+
model=model, model_version=mv,
233+
protocol=protocol, protocol_version=pv,
234+
fittingspec=fittingspec, fittingspec_version=fv,
235+
dataset=dataset,
236+
)
237+
assert user not in fr.viewers
238+
assert not fr.is_visible_to_user(user)
239+
240+
fr.model.add_collaborator(user)
241+
assert user not in fr.viewers
242+
assert not fr.is_visible_to_user(user)
243+
244+
fr.protocol.add_collaborator(user)
245+
assert user not in fr.viewers
246+
assert not fr.is_visible_to_user(user)
247+
248+
fr.fittingspec.add_collaborator(user)
249+
assert user in fr.viewers
250+
assert fr.is_visible_to_user(user)

0 commit comments

Comments
 (0)