Skip to content

Commit bd5e9e7

Browse files
committed
Add fitting comparison for datasets
1 parent 71b8aa9 commit bd5e9e7

8 files changed

Lines changed: 240 additions & 8 deletions

File tree

weblab/datasets/tests/test_views.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,3 +574,96 @@ def test_nonexistent_dataset_redirects_anonymous_to_login(self, client, helpers,
574574
def test_nonexistent_dataset_generates_404_for_user(self, client, logged_in_user, helpers, recipe, url):
575575
response = client.get(url % 10000)
576576
assert response.status_code == 404
577+
578+
579+
@pytest.mark.django_db
580+
class TestDatasetCompareFittingResultsView:
581+
def test_shows_fittings_related_to_dataset(self, client, fittingresult_version):
582+
fit = fittingresult_version.fittingresult
583+
584+
# should not be included, as it uses a different dataset
585+
recipes.fittingresult_version.make()
586+
587+
response = client.get('/datasets/%d/fittings' % fit.dataset.pk)
588+
589+
assert response.status_code == 200
590+
assert response.context['comparisons'] == [(fit.model, [fit])]
591+
592+
def test_groups_by_model(self, client, helpers, public_dataset):
593+
m1, m2 = recipes.model.make(_quantity=2)
594+
m1v = helpers.add_cached_version(m1, visibility='public')
595+
m2v = helpers.add_cached_version(m2, visibility='public')
596+
597+
# Create publicly visible fitting result versions
598+
fit1_m1 = recipes.fittingresult_version.make(
599+
fittingresult__dataset=public_dataset,
600+
fittingresult__model=m1,
601+
fittingresult__model_version=m1v,
602+
fittingresult__fittingspec_version__visibility='public',
603+
fittingresult__protocol_version__visibility='public',
604+
).fittingresult
605+
606+
fit2_m1 = recipes.fittingresult_version.make(
607+
fittingresult__dataset=public_dataset,
608+
fittingresult__model=m1,
609+
fittingresult__model_version=m1v,
610+
fittingresult__fittingspec_version__visibility='public',
611+
fittingresult__protocol_version__visibility='public',
612+
).fittingresult
613+
614+
fit3_m2 = recipes.fittingresult_version.make(
615+
fittingresult__dataset=public_dataset,
616+
fittingresult__model=m2,
617+
fittingresult__model_version=m2v,
618+
fittingresult__fittingspec_version__visibility='public',
619+
fittingresult__protocol_version__visibility='public',
620+
).fittingresult
621+
622+
response = client.get('/datasets/%d/fittings' % public_dataset.id)
623+
624+
assert response.status_code == 200
625+
assert response.context['comparisons'] == [
626+
(m1, [fit1_m1, fit2_m1]),
627+
(m2, [fit3_m2]),
628+
]
629+
630+
def test_multiple_model_versions_for_dataset(self, client, helpers, public_dataset):
631+
m1, m2 = recipes.model.make(_quantity=2)
632+
m1v1 = helpers.add_cached_version(m1, visibility='public')
633+
m1v2 = helpers.add_cached_version(m1, visibility='public')
634+
m2v = helpers.add_cached_version(m2, visibility='public')
635+
636+
# Create publicly visible fitting result versions
637+
fit1_m1v1 = recipes.fittingresult_version.make(
638+
fittingresult__dataset=public_dataset,
639+
fittingresult__model=m1,
640+
fittingresult__model_version=m1v1,
641+
fittingresult__fittingspec_version__visibility='public',
642+
fittingresult__protocol_version__visibility='public',
643+
).fittingresult
644+
645+
fit2_m1v2 = recipes.fittingresult_version.make(
646+
fittingresult__dataset=public_dataset,
647+
fittingresult__model=m1,
648+
fittingresult__model_version=m1v2,
649+
fittingresult__fittingspec_version__visibility='public',
650+
fittingresult__protocol_version__visibility='public',
651+
).fittingresult
652+
653+
fit3_m2v = recipes.fittingresult_version.make(
654+
fittingresult__dataset=public_dataset,
655+
fittingresult__model=m2,
656+
fittingresult__model_version=m2v,
657+
fittingresult__fittingspec_version__visibility='public',
658+
fittingresult__protocol_version__visibility='public',
659+
).fittingresult
660+
661+
response = client.get(
662+
'/datasets/%d/fittings' % public_dataset.id
663+
)
664+
665+
assert response.status_code == 200
666+
assert response.context['comparisons'] == [
667+
(m1, [fit1_m1v1, fit2_m1v2]),
668+
(m2, [fit3_m2v]),
669+
]

weblab/datasets/urls.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,10 @@
6868
views.DatasetRenameView.as_view(),
6969
name='rename',
7070
),
71+
72+
url(
73+
r'^(?P<pk>\d+)/fittings$',
74+
views.DatasetCompareFittingResultsView.as_view(),
75+
name='compare_fittings',
76+
),
7177
]

weblab/datasets/views.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import mimetypes
22
import os.path
3+
from itertools import groupby
34
from zipfile import ZipFile
45

56
from braces.views import UserFormKwargsMixin
@@ -23,6 +24,7 @@
2324

2425
from core.combine import ManifestWriter
2526
from core.visibility import VisibilityMixin
27+
from fitting.models import FittingResult
2628

2729
from .forms import (
2830
DatasetAddFilesForm,
@@ -283,3 +285,38 @@ def post(self, request, *args, **kwargs):
283285
def get_success_url(self, *args, **kwargs):
284286
ns = self.request.resolver_match.namespace
285287
return reverse(ns + ':detail', args=[self._get_object().id])
288+
289+
290+
class DatasetCompareFittingResultsView(DetailView):
291+
model = Dataset
292+
template_name = 'datasets/compare_fittings.html'
293+
294+
def _get_object(self):
295+
if not hasattr(self, 'object'):
296+
self.object = self.get_object()
297+
return self.object
298+
299+
def get_context_data(self, **kwargs):
300+
dataset = self._get_object()
301+
302+
fittings = FittingResult.objects.filter(
303+
dataset=dataset.pk,
304+
).select_related(
305+
'model',
306+
).order_by('model', '-model_version__timestamp')
307+
308+
# Ensure all are visible to user
309+
fittings = [
310+
fit for fit in fittings
311+
if fit.is_visible_to_user(self.request.user)
312+
]
313+
314+
def _sorted_fittings(fits):
315+
return sorted(list(fits), key=lambda x: x.id)
316+
317+
kwargs['comparisons'] = [
318+
(obj, _sorted_fittings(fits))
319+
for (obj, fits) in groupby(fittings, lambda fit: fit.model)
320+
]
321+
322+
return super().get_context_data(**kwargs)

weblab/entities/templatetags/entities.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,17 @@ def url_compare_experiments(entity, commit):
163163
return reverse(url_name, args=args)
164164

165165

166+
@register.filter
167+
def url_compare_fittings(entity, commit):
168+
"""Generate the view URL for comparing fitting experiments using
169+
a specific version of this entity
170+
"""
171+
url_name = 'entities:compare_fittings'
172+
last_tag = _url_friendly_label(entity, commit)
173+
args = [entity.url_type, entity.id, last_tag]
174+
return reverse(url_name, args=args)
175+
176+
166177
@register.filter
167178
def url_run_experiments(entity, commit):
168179
last_tag = _url_friendly_label(entity, commit)
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
{% extends "base.html" %}
2+
{% load entities %}
3+
{% load fittings %}
4+
{% load staticfiles %}
5+
6+
{% block title %}{{ type|capfirst }}: compare fitting experiments - {% endblock title %}
7+
8+
{% block content %}
9+
{% include "./includes/dataset_header.html" %}
10+
<div id="entitydetails"></div>
11+
12+
<div id="entityversion"
13+
data-version-href="{% url 'datasets:detail' dataset.id %}"
14+
data-version-json-href="{% url 'datasets:version_json' dataset.id %}">
15+
<div class="closebtn">
16+
</div>
17+
<div class="suppl">
18+
<small>
19+
Created <time id="entityversiontime">{{ dataset.created_at }}</time>
20+
by <em id="entityversionauthor">{{ dataset.author.full_name }}</em>.
21+
Visibility: <span>{{ dataset.visibility }}</span>
22+
<img src="{% static 'img/info.png' %}" alt="help" title="{{ VISIBILITY_HELP }}" />.
23+
24+
{% comment %}
25+
{% can_delete_entity version as can_delete %}
26+
{% if can_delete %}
27+
Delete experiment version:
28+
<a title="delete this version of this experiment" href="{% url 'experiments:delete_version' experiment.id version.id %}">
29+
<img src="{% static 'img/delete.png' %}" alt="delete this version of this experiment"
30+
title="delete this version of this experiment"/></a>
31+
{% endif %}
32+
{% endcomment %}
33+
34+
<br/>Corresponding protocol:
35+
<a href="{% url 'entities:detail' 'protocol' dataset.protocol.pk %}">{{ dataset.protocol.name }}</a>
36+
37+
</small>
38+
</div>
39+
<div id="entity-view-buttons" class="link-list">
40+
<a class="button" href="{% url "fitting:result:new" %}?dataset={{dataset.pk}}" title="Run fitting experiment using this dataset">Fit</a>
41+
</div>
42+
43+
<p id="description">
44+
{{ dataset.description }}
45+
</p>
46+
47+
<div id="experiment-files-switcher">
48+
</div>
49+
<div id="entityexperimentlist">
50+
<h3>Fitting Experiments run with this dataset</h3>
51+
<p>
52+
This {{type}} has been run with the following models.
53+
Click to view the latest results of each single fitting experiment, or select multiple fitting experiments to compare them.
54+
</p>
55+
56+
<div id="entityexperimentlistpartners">
57+
<ul>
58+
{% for model, fittings in comparisons %}
59+
60+
<li>
61+
{{ model }}
62+
<ul>
63+
{% for fitting in fittings %}
64+
<li>
65+
<input name='experimentVersionIds', value="{{ fitting.latest_version.id }}" type="checkbox">
66+
<a href="{% url 'fitting:result:version' fitting.id fitting.latest_version.id %}">
67+
{{ fitting|name_of_fittingspec }}
68+
</a>
69+
</li>
70+
{% endfor %}
71+
</ul>
72+
</li>
73+
{% endfor %}
74+
</ul>
75+
</div>
76+
77+
<div id="entityexperimentlistpartnersact">
78+
[<a id="entityexperimentlistpartnersactall">select all</a>]
79+
<span id="entityexperimentlist_span_latest">[<a id="entityexperimentlistpartnersactlatest">select latest</a>]</span>
80+
[<a id="entityexperimentlistpartnersactnone">select none</a>]
81+
<br/>
82+
<button id="entityexperimentlist_showallversions">show all versions</button>
83+
<button id="entityexperimentlistpartnersactcompare" data-base-href="{% url_fitting_comparison_base %}">compare selected fittings</button>
84+
</div>
85+
</div>
86+
</div>
87+
{% endblock content %}

weblab/templates/datasets/dataset_detail.html

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,15 @@
4040
</div>
4141
<div id="entity-view-buttons" class="link-list">
4242
<a class="button" href="{% url "fitting:result:new" %}?dataset={{dataset.pk}}" title="Run fitting experiment using this dataset">Fit</a>
43+
<a class="button" href="{% url "datasets:compare_fittings" dataset.pk %}" title="Show results of parameter fitting experiments run using this dataset">Fittings</a>
44+
<br/>
4345
</div>
4446

4547
<p id="description">
4648
{{ dataset.description }}
4749
</p>
4850

4951
<div id="experiment-files-switcher">
50-
{% comment %}
51-
<button style="margin-left:5px; float:left;" id="experiment-files-switcher-exp" title="View &amp; compare experiments using this ${entity.type}">Compare ${otherType}s</button>
52-
<br/>
53-
{% endcomment %}
5452
</div>
5553

5654
<div id="entityversiondetails">

weblab/templates/datasets/includes/dataset_header.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
{% load datasets %}
33

44
<h1 id="entityname">
5+
<a href="{% url 'datasets:detail' dataset.id %}">
56
<small>Dataset: </small><span>{{ dataset.name }}</span>
7+
</a>
68
</h1>
79

810
<div class="suppl">

weblab/templates/entities/entity_version.html

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ <h2 id="entityversionname">
2525
</h2>
2626

2727
<div class="suppl">
28-
<small>
28+
<small'>
2929
Created <time id="entityversiontime">{{ version.timestamp }}</time>
3030
by <em id="entityversionauthor">{{ version.author }}</em>.
3131
<div id="versionVisibility" data-change-href="{% entity_version_url 'change_visibility' entity version %}">
@@ -70,9 +70,7 @@ <h2 id="entityversionname">
7070
{% elif entity.entity_type == 'fittingspec' %}
7171
<a class="button" href="{% url "fitting:result:new" %}?fittingspec_version={{version.pk}}" title="Run fitting experiment using this specification">Fit</a>
7272
{% endif %}
73-
<!--
74-
<a class="button" href="" title="Show results of parameter fitting experiments run {% if type == 'model' %}on this model{% else %}using this protocol{% endif %}">Fittings</a>
75-
-->
73+
<a class="button" href="{% entity_version_url 'compare_fittings' entity version %}" title="Show results of parameter fitting experiments run {% if type == 'model' %}on this model{% else %}using this protocol{% endif %}">Fittings</a>
7674
</div>
7775

7876
{% block content_detail %}

0 commit comments

Comments
 (0)