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

Managements views for algorithm interfaces for phases #3812

Merged
merged 12 commits into from
Feb 5, 2025
4 changes: 4 additions & 0 deletions app/config/urls/challenge_subdomain.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
),
name="subdomain_robots_txt",
),
path(
"components/",
include("grandchallenge.components.urls", namespace="components"),
),
amickan marked this conversation as resolved.
Show resolved Hide resolved
path(
"evaluation/",
include("grandchallenge.evaluation.urls", namespace="evaluation"),
Expand Down
14 changes: 14 additions & 0 deletions app/grandchallenge/algorithms/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,20 @@ def algorithm_interface_manager(self):
def algorithm_interface_through_model_manager(self):
return AlgorithmAlgorithmInterface.objects.filter(algorithm=self)

@property
def algorithm_interface_create_url(self):
return reverse(
"algorithms:interface-create", kwargs={"slug": self.slug}
)

@property
def algorithm_interface_delete_viewname(self):
return "algorithms:interface-delete"

@property
def algorithm_interface_list_url(self):
return reverse("algorithms:interface-list", kwargs={"slug": self.slug})

def is_editor(self, user):
return user.groups.filter(pk=self.editors_group.pk).exists()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
<a class="nav-link"
href="{% url 'algorithms:interface-list' slug=object.slug %}"
><i class="fas fa-sliders-h fa-fw"></i>&nbsp;Interfaces
{% if not object.interfaces.all %}&nbsp;
<i class="fas fa-exclamation-triangle text-danger"></i>
{% endif %}
</a>
<a class="nav-link" id="v-pills-templates-tab" data-toggle="pill"
href="#templates" role="tab"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,5 @@
{% endblock %}

{% block content %}

<h2>Confirm Algorithm Interface Deletion</h2>
<form action="" method="post">
{% csrf_token %}
<p>Are you sure that you want to delete the following algorithm interface from your algorithm?</p>
<ul>
<li>Inputs: {{ object.interface.inputs.all|oxford_comma }}</li>
<li>Outputs: {{ object.interface.outputs.all|oxford_comma }}</li>
</ul>
<p>
<b class="text-danger">WARNING: You are not able to undo this action. Once the interface is deleted, it is deleted forever.</b>
</p>
<a href="{% url 'algorithms:interface-list' slug=algorithm.slug %}"
type="button"
class="btn btn-secondary">Cancel</a>
<input type="submit"
value="I understand, delete interface"
class="btn btn-danger"/>
</form>

{% include 'algorithms/partials/algorithminterface_confirm_delete.html' with base_obj=algorithm %}
{% endblock %}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% load remove_whitespace %}

{% block title %}
Interfaces - {{ algorithm.title }} - {{ block.super }}
Expand All @@ -22,36 +20,5 @@
{% endblock %}

{% block content %}
<h2>Algorithm Interfaces for {{ algorithm }}</h2>

<p>
The following interfaces are configured for your algorithm:
</p>
<p><a class="btn btn-primary" href="{% url 'algorithms:interface-create' slug=algorithm.slug %}">Add new interface</a></p>

<div class="table-responsive">
<table class="table table-hover table-borderless">
<thead class="thead-light">
<th>Inputs</th>
<th>Outputs</th>
<th>Delete</th>
</thead>
<tbody>
{% for object in object_list %}
<tr>
<td>{{ object.interface.inputs.all|oxford_comma }}</td>
<td>{{ object.interface.outputs.all|oxford_comma }}</td>
<td>
<a class="btn btn-sm btn-danger m-0"
href="{% url 'algorithms:interface-delete' slug=algorithm.slug interface_pk=object.interface.pk %}">
<i class="fas fa-trash"></i>
</a>
</td>
</tr>
{% empty %}
<tr><td colspan="100%" class="text-center">This algorithm does not have any interfaces defined yet.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
{% include 'algorithms/partials/algorithminterface_list.html' with base_obj=algorithm %}
{% endblock %}
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
{% extends "base.html" %}
{% load crispy_forms_tags %}

{% block title %}
Create Interface - {{ algorithm.title }} - {{ block.super }}
Create Interface - {{ base_obj.title }} - {{ block.super }}
{% endblock %}

{% block breadcrumbs %}
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'algorithms:list' %}">Algorithms</a>
</li>
<li class="breadcrumb-item"><a
href="{{ algorithm.get_absolute_url }}">{{ algorithm.title }}
href="{{ algorithm.get_absolute_url }}">{{ base_obj.title }}
</a></li>
<li class="breadcrumb-item"><a
href="{% url 'algorithms:interface-list' slug=algorithm.slug %}">Interfaces
href="{% url 'algorithms:interface-list' slug=base_obj.slug %}">Interfaces
</a></li>
<li class="breadcrumb-item active"
aria-current="page">Create Interface
Expand All @@ -22,21 +21,5 @@
{% endblock %}

{% block content %}

<h2>Create An Algorithm Interface</h2>
<br>
<p>
Create an interface for your algorithm: define any combination of inputs and outputs, and optionally mark the interface as default for the algorithm.
</p>
<p>
Please see the <a href="{% url 'components:component-interface-list-input' %}">list of input options</a> and the <a href="{% url 'components:component-interface-list-output' %}">
list of output options
</a> for more information and examples.
</p>
<p>
If you cannot find suitable inputs or outputs, please contact <a href="mailto:[email protected]">[email protected]</a>.
</p>

{% crispy form %}

{% include 'algorithms/partials/algorithminterface_form.html' %}
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{% load remove_whitespace %}

<h2>Confirm Algorithm Interface Deletion</h2>
<form action="" method="post">
{% csrf_token %}
<p>Are you sure that you want to delete the following algorithm interface from this {{ base_obj.verbose_name }}?</p>
<ul>
<li>Inputs: {{ object.interface.inputs.all|oxford_comma }}</li>
<li>Outputs: {{ object.interface.outputs.all|oxford_comma }}</li>
</ul>
<p>
<b class="text-danger">WARNING: You are not able to undo this action. Once the interface is deleted, it is deleted forever.</b>
</p>
<a href="{{ base_obj.algorithm_interface_list_url }}"
type="button"
class="btn btn-secondary">Cancel</a>
<input type="submit"
value="I understand, delete interface"
class="btn btn-danger"/>
</form>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% load crispy_forms_tags %}
{% load meta_attr %}

<h2>Create An Algorithm Interface</h2>
<p>
Create an interface: define any combination of inputs and outputs, and optionally mark the interface as default for the {{ base_obj|verbose_name }}.</p>
<p>
Please see the <a href="{% url 'components:component-interface-list-input' %}">list of input options</a> and the <a href="{% url 'components:component-interface-list-output' %}">
list of output options </a> for more information and examples.
</p>
<p>
If you cannot find suitable inputs or outputs, please contact <a href="mailto:[email protected]">[email protected]</a>.
</p>

{% crispy form %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{% load crispy_forms_tags %}
{% load remove_whitespace %}
{% load meta_attr %}

<h2>Algorithm Interfaces for {{ base_obj }}</h2>

<p>The following interfaces are configured for your {{ base_obj|verbose_name }}:</p>
<p>
<a class="btn btn-primary" href="{{ base_obj.algorithm_interface_create_url }}">Add new interface</a>
</p>

<div class="table-responsive">
<table class="table table-hover table-borderless">
<thead class="thead-light">
<th>Inputs</th>
<th>Outputs</th>
<th>Delete</th>
</thead>
<tbody>
{% for object in object_list %}
<tr>
<td>{{ object.interface.inputs.all|oxford_comma }}</td>
<td>{{ object.interface.outputs.all|oxford_comma }}</td>
<td>
<a class="btn btn-sm btn-danger m-0"
href="{% url base_obj.algorithm_interface_delete_viewname slug=base_obj.slug interface_pk=object.interface.pk %}">
<i class="fas fa-trash"></i>
</a>
</td>
</tr>
{% empty %}
<tr><td colspan="100%" class="text-center">This {{ base_obj|verbose_name }} does not have any interfaces defined yet.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
31 changes: 22 additions & 9 deletions app/grandchallenge/algorithms/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1175,30 +1175,43 @@ def dispatch(self, request, *args, **kwargs):
return self.handle_no_permission()


class AlgorithmInterfaceForAlgorithmCreate(
AlgorithmInterfacePermissionMixin, CreateView
):
class AlgorithmInterfaceCreateBase(CreateView):
model = AlgorithmInterface
form_class = AlgorithmInterfaceForm
success_message = "Algorithm interface successfully added"

def get_success_url(self):
return reverse(
"algorithms:interface-list",
kwargs={"slug": self.algorithm.slug},
)
return NotImplementedError

@property
def base_obj(self):
return NotImplementedError

def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context.update({"algorithm": self.algorithm})
context.update({"base_obj": self.base_obj})
return context

def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({"base_obj": self.algorithm})
kwargs.update({"base_obj": self.base_obj})
return kwargs


class AlgorithmInterfaceForAlgorithmCreate(
AlgorithmInterfacePermissionMixin, AlgorithmInterfaceCreateBase
):
@property
def base_obj(self):
return self.algorithm

def get_success_url(self):
return reverse(
"algorithms:interface-list",
kwargs={"slug": self.algorithm.slug},
)


class AlgorithmInterfacesForAlgorithmList(
AlgorithmInterfacePermissionMixin, ListView
):
Expand Down
18 changes: 18 additions & 0 deletions app/grandchallenge/evaluation/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1212,6 +1212,24 @@ def algorithm_interface_manager(self):
def algorithm_interface_through_model_manager(self):
return PhaseAlgorithmInterface.objects.filter(phase=self)

@property
def algorithm_interface_create_url(self):
return reverse(
"evaluation:interface-create",
kwargs={"challenge_short_name": self.challenge, "slug": self.slug},
)

@property
def algorithm_interface_delete_viewname(self):
return "evaluation:interface-delete"

@property
def algorithm_interface_list_url(self):
return reverse(
"evaluation:interface-list",
kwargs={"challenge_short_name": self.challenge, "slug": self.slug},
)


class PhaseUserObjectPermission(UserObjectPermissionBase):
content_object = models.ForeignKey(Phase, on_delete=models.CASCADE)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{% extends "pages/challenge_settings_base.html" %}
{% load url %}
{% load static %}

{% block title %}
Create Interface - {{ base_obj.title }} - {% firstof challenge.title challenge.short_name %} - {{ block.super }}
{% endblock %}

{% block breadcrumbs %}
<ol class="breadcrumb">
<li class="breadcrumb-item"><a
href="{% url 'challenges:list' %}">Challenges</a>
</li>
<li class="breadcrumb-item"><a
href="{{ challenge.get_absolute_url }}">{% firstof challenge.title challenge.short_name %}</a></li>
<li class="breadcrumb-item"><a
href="{% url 'evaluation:interface-list' challenge_short_name=challenge.short_name slug=base_obj.slug %}">Interfaces
</a></li>
<li class="breadcrumb-item active"
aria-current="page">Create interface</li>
</ol>
{% endblock %}

{% block content %}
{% include 'algorithms/partials/algorithminterface_form.html' %}
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{% extends "pages/challenge_settings_base.html" %}
{% load url %}
{% load static %}

{% block title %}
Delete interface - {{ phase.title }} - {% firstof challenge.title challenge.short_name %} - {{ block.super }}
{% endblock %}

{% block breadcrumbs %}
<ol class="breadcrumb">
<li class="breadcrumb-item"><a
href="{% url 'challenges:list' %}">Challenges</a>
</li>
<li class="breadcrumb-item"><a
href="{{ challenge.get_absolute_url }}">{% firstof challenge.title challenge.short_name %}</a></li>
<li class="breadcrumb-item"><a
href="{% url 'evaluation:interface-list' challenge_short_name=challenge.short_name slug=phase.slug %}">Interfaces
</a></li>
<li class="breadcrumb-item active"
aria-current="page">Delete interface</li>
</ol>
{% endblock %}

{% block content %}
{% include 'algorithms/partials/algorithminterface_confirm_delete.html' with base_obj=phase %}
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{% extends "pages/challenge_settings_base.html" %}
{% load url %}
{% load static %}

{% block title %}
Algorithm interfaces for {{ phase.title }} - {% firstof challenge.title challenge.short_name %} - {{ block.super }}
{% endblock %}

{% block breadcrumbs %}
<ol class="breadcrumb">
<li class="breadcrumb-item"><a
href="{% url 'challenges:list' %}">Challenges</a>
</li>
<li class="breadcrumb-item"><a
href="{{ challenge.get_absolute_url }}">{% firstof challenge.title challenge.short_name %}</a></li>
<li class="breadcrumb-item active"
aria-current="page">Algorithm Interfaces for {{ phase.title }}</li>
</ol>
{% endblock %}

{% block content %}
{% include 'algorithms/partials/algorithminterface_list.html' with base_obj=phase %}
{% endblock %}
Loading