Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework Component Interface Value representations #3364

Merged
merged 41 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
8a69000
First version of single-line representation of CIVs
chrisvanrun May 28, 2024
a27eeee
Minor fixes
chrisvanrun May 28, 2024
1da9ddd
Add interface rich algorithm job fixture
chrisvanrun May 28, 2024
107394e
Improve fixtures building
chrisvanrun May 30, 2024
80827f7
Initial version of CIV rendering via templates
chrisvanrun May 30, 2024
88d041e
Refactor civ filters into simple tags
chrisvanrun May 30, 2024
27ebfd2
Push CIV fixtures to seperate script, add a bunch of additional values
chrisvanrun May 30, 2024
fccda54
Check actual holders instead of theoretical super kinds
chrisvanrun May 30, 2024
3c231ce
Make fallback a warning badge
chrisvanrun May 30, 2024
46a1168
Add sorting of civs
chrisvanrun May 30, 2024
bb487cf
Slightly differ inline and regular image/file renders
chrisvanrun May 30, 2024
512989e
Minor rename
chrisvanrun May 31, 2024
1a0dbee
Limit value pre height
chrisvanrun May 31, 2024
a5316fd
Add border to long value repr
chrisvanrun May 31, 2024
a8b3421
Add lazy loading for vega charts
chrisvanrun May 31, 2024
4eb46ac
Improve naming partial templates
chrisvanrun May 31, 2024
b7aa456
Grammar
chrisvanrun May 31, 2024
baa318a
Cleanup context generators
chrisvanrun May 31, 2024
186b5b5
Apply civ tag to evaluation detail
chrisvanrun May 31, 2024
6b7bae5
Correct file suffix for json fixtures
chrisvanrun May 31, 2024
eee4174
Add render tests for civ tags
chrisvanrun May 31, 2024
918474c
Fix using incorrect object to pull url from
chrisvanrun May 31, 2024
e485406
Minor padding on icons
chrisvanrun May 31, 2024
d4167b3
Minor adjust ment on when to place borders on value repr
chrisvanrun May 31, 2024
0404ec4
Add action word to badges
chrisvanrun May 31, 2024
641f837
Add action word to file badge
chrisvanrun May 31, 2024
0cc5b97
Add a make command for component_interface_value_fixtures
chrisvanrun Jun 4, 2024
b7124c4
Merge branch 'main' into civ-repr
chrisvanrun Jun 6, 2024
3e3c970
Combine rendering of charts into a single module
chrisvanrun Jun 6, 2024
b81b7d7
Rework templatetag civ rendering into pure templates
chrisvanrun Jun 6, 2024
6f6fb6d
Fix thumbnails not being rendered
chrisvanrun Jun 6, 2024
cd0dd76
Minor spacing
chrisvanrun Jun 6, 2024
b0fa72e
Fix not loading filter
chrisvanrun Jun 6, 2024
a3cec3c
Create a test for new civ templates
chrisvanrun Jun 6, 2024
d696332
Grammar
chrisvanrun Jun 6, 2024
3954aa0
Create valid CIVs for testing civ templates
chrisvanrun Jun 6, 2024
edccd8d
Remove superflous json_dumps filter
chrisvanrun Jun 6, 2024
ee5f7e4
Merge branch 'main' into civ-repr
chrisvanrun Jun 6, 2024
4bc74dc
Update app/grandchallenge/components/templates/components/partials/ci…
chrisvanrun Jun 10, 2024
8a61895
Collapse civ / civ_inline templating into a single template
chrisvanrun Jun 10, 2024
843c89d
Push interface kind checks to models
chrisvanrun Jun 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -104,22 +104,27 @@ development_fixtures:
celery_worker_evaluation \
bash -c "python manage.py migrate && python manage.py runscript minio development_fixtures"


algorithm_evaluation_fixtures:
docker compose run \
-v $(shell readlink -f ./scripts/):/app/scripts:ro \
--rm \
celery_worker_evaluation \
python manage.py runscript algorithm_evaluation_fixtures


cost_fixtures:
docker compose run \
-v $(shell readlink -f ./scripts/):/app/scripts:ro \
--rm \
web \
python manage.py runscript cost_fixtures

component_interface_value_fixtures:
docker compose run \
-v $(shell readlink -f ./scripts/):/app/scripts:ro \
--rm \
celery_worker_evaluation \
python manage.py runscript component_interface_value_fixtures

superuser:
docker compose run \
-v $(shell readlink -f ./scripts/):/app/scripts:ro \
Expand Down
128 changes: 27 additions & 101 deletions app/grandchallenge/algorithms/templates/algorithms/job_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
{% load static %}
{% load url %}
{% load meta_attr %}
{% load evaluation_extras %}
{% load json %}
{% load string %}
{% load civ %}

{% block title %}
Algorithm Result - {{ block.super }}
Expand Down Expand Up @@ -68,49 +70,17 @@
<h2>Algorithm Result</h2>

{{ object.rendered_result_text }}
<br>
{% if json %}
<table class="table table-borderless table-hover table-sm">
<thead class="thead-light">
<tr>
<th>Metric</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{% for object in json %}
<tr>
<td>{{ object.interface.title }}</td>
<td>{{ object.value }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}

{% for chart in charts %}
<h3 class="mt-3">{{ chart.interface.title }}</h3>
<div class="w-100 vega-lite-chart">
{{ chart.value|json_script:chart.pk }}
</div>
{% endfor %}

{% for thumbnail in thumbnails %}
<h3 class="mt-3">{{ thumbnail.interface.title }}</h3>
<div class="row">
<img class="m-3" height="400"
src="{{ thumbnail.file.url }}"
alt="{{ thumbnail.file.name }}" loading="lazy">
</div>
{% endfor %}

{% for file in files %}
<h3 class="mt-3">{{ file.interface.title }}</h3>
<div><a class="btn btn-primary mb-3"
href="{{ file.file.url }}">
<i class="fa fa-download"></i>&nbsp;Download {{ file.interface.kind }}
</a></div>
{% endfor %}
<dl>
{% for output in object.outputs.all|sort_civs %}
{% if output.interface.slug != 'results-json-file' or not object.rendered_result_text %}
<dt class="mt-3">{{ output.interface.title }}</dt>
<dd>
{% include 'components/partials/civ.html' with object=output only %}
</dd>
{% endif %}
{% endfor %}
</dl>

{% if object.status == object.SUCCESS %}
<button class="btn btn-primary my-3"
Expand Down Expand Up @@ -191,32 +161,14 @@ <h3>Result Reference Data</h3>
<dt class="col-sm-3">Inputs</dt>
<dd class="col-sm-9">
<ul class="list-unstyled mb-0">
{% for input in object.inputs.all %}
{% for file in input.image.files.all %}
<li>
<a class="badge badge-primary"
href="{{ file.file.url }}">
<i class="fa fa-download"></i>
Input {{ forloop.parentloop.counter }} {{ input.image.name|stem }}
({{ file.file|suffix }})
</a>
</li>
{% endfor %}
{% if input.file %}
<li>
<a class="badge badge-primary"
href="{{ input.file.url }}" download="{{ input.file.name|name }}">
<i class="fa fa-download"></i>
Input {{ forloop.parentloop.counter }} {{ input.file.name|stem }}
({{ input.file|suffix }})
</a>
</li>
{% endif %}
{% if input.value is not None %}
<li>
{{ input.interface.slug }}: {{ input.value|truncatechars_html:1000 }}
</li>
{% endif %}
{% for input in object.inputs.all|sort_civs %}
<li>
{% include 'components/partials/civ.html' with object=input display_inline=True only %}
</li>
{% empty %}
<li>
<i class="fa fa-eye-slash text-warning"></i> No inputs were found
</li>
{% endfor %}
</ul>
</dd>
Expand All @@ -225,40 +177,14 @@ <h3>Result Reference Data</h3>
<dt class="col-sm-3">Outputs</dt>
<dd class="col-sm-9">
<ul class="list-unstyled mb-0">
{% for output in object.outputs.all %}
{% if output.value is not None %}
<li>
<a href="#resultInfoModal"
data-toggle="modal"
data-target="#resultInfoModal"
data-title="Result Info"
data-output="{{ output.value }}"
data-pk="{{ object.pk }}"
class="badge badge-primary">
<i class="fa fa-download"></i>
{{ output.interface.title }}
</a>
</li>
{% elif output.file %}
<li>
<a class="badge badge-primary"
href="{{ output.file.url }}">
<i class="fa fa-download"></i> {{ output.interface.title }}
</a>
</li>
{% elif output.image %}
{% for file in output.image.files.all %}
<li>
<a class="badge badge-primary" href="{{ file.file.url }}">
<i class="fa fa-download"></i>
{{ output.interface.title }}{% if forloop.first and forloop.last %}{%else %} ({{forloop.counter }}){% endif %}
</a>
</li>
{% endfor %}
{% endif %}

{% for output in object.outputs.all|sort_civs %}
<li>
{% include 'components/partials/civ.html' with object=output display_inline=True only %}
</li>
{% empty %}
<li>
<i class="fa fa-eye-slash text-warning"></i> No output images were found
<i class="fa fa-eye-slash text-warning"></i> No outputs were found
</li>
{% endfor %}
</ul>
Expand Down
38 changes: 0 additions & 38 deletions app/grandchallenge/algorithms/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -776,48 +776,10 @@ def get_context_data(self, **kwargs):
viewers_form = ViewersForm()
viewers_form.fields["action"].initial = ViewersForm.REMOVE

files = []
thumbnails = []
charts = []
json = []
for output in self.object.outputs.all():
if (
output.interface.kind
== InterfaceKind.InterfaceKindChoices.CHART
):
charts.append(output)
elif output.interface.kind in [
InterfaceKind.InterfaceKindChoices.PDF,
InterfaceKind.InterfaceKindChoices.CSV,
InterfaceKind.InterfaceKindChoices.ZIP,
InterfaceKind.InterfaceKindChoices.SQREG,
]:
files.append(output)
elif output.interface.kind in [
InterfaceKind.InterfaceKindChoices.THUMBNAIL_PNG,
InterfaceKind.InterfaceKindChoices.THUMBNAIL_JPG,
]:
thumbnails.append(output)
elif (
output.interface.kind
in [
InterfaceKind.InterfaceKindChoices.BOOL,
InterfaceKind.InterfaceKindChoices.FLOAT,
InterfaceKind.InterfaceKindChoices.INTEGER,
InterfaceKind.InterfaceKindChoices.STRING,
]
and output.interface.store_in_database
):
json.append(output)

context.update(
{
"viewers_form": viewers_form,
"job_perms": get_perms(self.request.user, self.object),
"charts": charts,
"files": files,
"thumbnails": thumbnails,
"json": json,
}
)

Expand Down
39 changes: 39 additions & 0 deletions app/grandchallenge/charts/static/js/charts/render_charts.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,42 @@ document.addEventListener("DOMContentLoaded", function(event) {
renderVegaLiteChart(element);
}
});


// Lazy rendering
// Tag the containers with the class 'vega-lite-chart-lazy' to only render
const handledAttribute = "data-vega-chart-is-rendered";

function handleInterSection(entries) {
entries.forEach((entry) => {
if (entry.intersectionRatio < 0.40) {
return;
}

const element = entry.target;


if (element.getAttribute(handledAttribute)) {
return;
}

renderVegaLiteChart(element);

// Tag the element as being handled
element.setAttribute(handledAttribute, "");
this.unobserve(element);
}
)
}

const observer = new IntersectionObserver(handleInterSection, {
rootMargin: "0px",
threshold: 1.0,
});


document.addEventListener("DOMContentLoaded", function(event) {
for (const element of document.getElementsByClassName("vega-lite-chart-lazy")) {
observer.observe(element, );
}
});
17 changes: 17 additions & 0 deletions app/grandchallenge/components/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,7 @@ class Meta:

class ComponentInterface(OverlaySegmentsMixin):
Kind = InterfaceKind.InterfaceKindChoices
SuperKind = InterfaceSuperKindChoices

title = models.CharField(
max_length=255,
Expand Down Expand Up @@ -898,6 +899,22 @@ def is_json_kind(self):
def is_file_kind(self):
return self.kind in InterfaceKind.interface_type_file()

@property
def is_thumbnail_kind(self):
return self.kind in [
InterfaceKindChoices.THUMBNAIL_JPG,
InterfaceKindChoices.THUMBNAIL_PNG,
]

@property
def is_previewable(self):
return self.store_in_database and self.kind in [
InterfaceKindChoices.BOOL,
InterfaceKindChoices.FLOAT,
InterfaceKindChoices.INTEGER,
InterfaceKindChoices.STRING,
]

@property
def super_kind(self):
if self.saved_in_object_store:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{% load civ %}


{% with interface=object.interface civ=object %}

{% if interface.super_kind == interface.SuperKind.VALUE %}
{# No fallback here as a value could be intentionally null #}
{% if interface.kind == interface.Kind.CHART %}
{% include "components/partials/civ/value_chart.html" %}
{% else %}
{% include "components/partials/civ/value.html" %}
{% endif %}
{% elif interface.super_kind == interface.SuperKind.FILE %}
{% if not civ.file %}
{% include "components/partials/civ/fallback.html" %}
{% elif civ.interface.is_thumbnail_kind %}
{% include "components/partials/civ/file_thumbnail.html" %}
{% else %}
{% include "components/partials/civ/file.html" %}
{% endif %}
{% elif interface.super_kind == interface.SuperKind.IMAGE %}
{% if not civ.image %}
{% include "components/partials/civ/fallback.html" %}
{% else %}
{% include "components/partials/civ/image.html" %}
{% endif %}
{% endif %}

{% endwith %}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{% block content %}{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div class="badge badge-warning">
{{ civ.interface.title }}: cannot be displayed
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{% load pathlib %}

<a class="badge badge-primary"
href="{{ civ.file.url }}"
download="{{ civ.file.name|name }}"
title="{{ civ.interface.title }}: {{ civ.file.name|name }}"
>
<i class="fa fa-download mr-1"
style="width:0.8rem;"
></i>
{% if display_inline %}
Download {{ civ.interface.title | truncatechars:64 }}:
{{ civ.file.name|name | truncatechars:30 }}
{% else %}
Download {{ civ.file.name|name }}
{% endif %}
</a>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{% extends display_inline|yesno:"components/partials/civ/modal_base.html,components/partials/civ/base.html" %}


{% block content %}
<img class="m-3" height="400"
src="{{ civ.file.url }}"
alt="{{ civ.file.name }}"
loading="lazy"
>
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{% load pathlib %}

<ul class="list-unstyled mb-0">
{% for file in civ.image.files.all %}
<li>
<a class="badge badge-primary"
href="{{ file.file.url }}"
title="{{ civ.interface.title }}: {{ forloop.parentloop.counter }}"
>
<i class="fa fa-download mr-1" style="width: 0.8rem"></i>
{% if display_inline %}
Download {{ civ.interface.title|truncatechars:64 }}:
{{ civ.file.name|name|truncatechars:30 }}
{% else %}
Download {{ civ.interface.title|truncatechars:64 }}:
{{ civ.image.name|stem|truncatechars:30 }}
({{ file.file|suffix|truncatechars:30 }})
{% endif %}
</a>
</li>
{% endfor %}
</ul>
Loading