From 90f270c82390e368b6676b1b70b62ff1cb6d603b Mon Sep 17 00:00:00 2001
From: Julien FumeyY <julien.fumey@pasteur.fr>
Date: Thu, 24 Apr 2025 12:01:27 +0200
Subject: [PATCH] add listview for simulatorjob

---
 src/InSillyCloWeb/assemblies/models.py        | 32 ++++++-----
 .../assemblies/simulatorjob_card.html         | 53 +++++++++++++++++++
 .../assemblies/simulatorjob_list.html         | 20 +++++++
 src/InSillyCloWeb/assemblies/urls.py          |  1 +
 src/InSillyCloWeb/assemblies/views.py         | 17 ++++++
 5 files changed, 109 insertions(+), 14 deletions(-)
 create mode 100644 src/InSillyCloWeb/assemblies/templates/assemblies/simulatorjob_card.html
 create mode 100644 src/InSillyCloWeb/assemblies/templates/assemblies/simulatorjob_list.html

diff --git a/src/InSillyCloWeb/assemblies/models.py b/src/InSillyCloWeb/assemblies/models.py
index b9a8003..785a029 100644
--- a/src/InSillyCloWeb/assemblies/models.py
+++ b/src/InSillyCloWeb/assemblies/models.py
@@ -288,6 +288,20 @@ class SimulatorJob(models.Model):
         null=True,
         blank=True,
     )
+    owner = models.ForeignKey(
+        User,
+        on_delete=models.CASCADE,
+        related_name='jobs',
+        null=True,
+        blank=True,
+    )
+    owning_session = models.ForeignKey(
+        Session,
+        on_delete=models.CASCADE,
+        related_name='jobs',
+        null=True,
+        blank=True,
+    )
     status = models.IntegerField(choices=JobStatus.choices, default=JobStatus.NEW)
     created_at = models.DateTimeField(auto_now_add=True)
     updated_at = models.DateTimeField(auto_now=True)
@@ -321,20 +335,6 @@ class SimulatorJob(models.Model):
         verbose_name="Restriction enzyme",
         help_text="Enzyme used to produce digestion gel",
     )
-    owner = models.ForeignKey(
-        User,
-        on_delete=models.CASCADE,
-        related_name='jobs',
-        null=True,
-        blank=True,
-    )
-    owning_session = models.ForeignKey(
-        Session,
-        on_delete=models.CASCADE,
-        related_name='jobs',
-        null=True,
-        blank=True,
-    )
 
     #########################################################################
     # Function and methods
@@ -442,6 +442,10 @@ class SimulatorJob(models.Model):
             messages.error(request, utils.clear_sensitive_info(str(e), self))
         return output
 
+    @property
+    def output_name(self):
+        pass
+
     @property
     def db_ip_files(self):
         return [self.dbip_dir / file for file in os.listdir(self.dbip_dir)]
diff --git a/src/InSillyCloWeb/assemblies/templates/assemblies/simulatorjob_card.html b/src/InSillyCloWeb/assemblies/templates/assemblies/simulatorjob_card.html
new file mode 100644
index 0000000..3a10920
--- /dev/null
+++ b/src/InSillyCloWeb/assemblies/templates/assemblies/simulatorjob_card.html
@@ -0,0 +1,53 @@
+{% load assemblies_tags %}
+{% load i18n %}
+
+<div class="card context-simulator">
+    <div class="card-body row">
+        <a class="col-12 col-xl-2 col-lg-3 col-md-3 me-md-4 text-decoration-none"  href="{% url 'assemblies:simulator-detail' uuid=object.uuid %}">
+            <div class="fw-bolder">
+                Job #{{object.uuid_short}}
+            </div>
+            <small class="text-secondary">
+                {{object|field_verbose_name:'updated_at'}}{%trans ':'%}<br/>
+                {{object.updated_at}}
+            </small>
+        </a>
+        <div class="col border-10 border-start border-primary ps-md-4">
+             
+            <div class="d-block">
+                <span class="fw-bolder">
+                    {{object|field_verbose_name_and_help_text:'restriction_enzyme_gel'}}
+                    {%trans ':'%}
+                </span>
+                {{object.restriction_enzyme_gel}}
+            </div>
+
+            
+            <div class="d-block">
+                <span class="fw-bolder">
+                    {{object|field_verbose_name_and_help_text:'pcr_pairs_str'}}
+                    {%trans ':'%}
+                </span>
+                {{object.pcr_pairs_str}}
+            </div>
+
+            <div class="d-block mt-1">
+                <span class="fw-bolder">
+                    {% trans "Plasmids"%}
+                {%for ip in object.input_parts.all %}
+                <div class="d-inline-flex px-3 py-0 mb-1 border border-{{ip.is_mandatory|yesno:'3,1'}}
+                rounded-5 border-designer"
+                    >{{ip}}</div>
+                {%endfor%}
+            </div>
+        </div>
+
+        <div class="col-auto">
+            <a href=""
+            class="btn btn-designer btn-md">
+                <i class="bi bi-cloud-arrow-down fs-3"></i>
+            </a>
+        </div>
+        
+    </div>
+</div>
diff --git a/src/InSillyCloWeb/assemblies/templates/assemblies/simulatorjob_list.html b/src/InSillyCloWeb/assemblies/templates/assemblies/simulatorjob_list.html
new file mode 100644
index 0000000..eef42dc
--- /dev/null
+++ b/src/InSillyCloWeb/assemblies/templates/assemblies/simulatorjob_list.html
@@ -0,0 +1,20 @@
+{% extends "assemblies/base.html" %}
+{% load crispy_forms_tags %}
+{% load i18n %}
+{% load djangoscss %}
+{% load assemblies_tags %}
+{% load include_markdown %}
+{% load sstatic %}
+
+{% block title %}{{object_list|class_verbose_name_plural}}{% endblock %}
+{% block page_title %}{{object_list|class_verbose_name_plural}}{% endblock %}
+
+{% block content %}
+{% for object in object_list %}
+    <div class="col-12 mb-4">
+        {% include 'assemblies/simulatorjob_card.html' with object=object%}
+    </div>
+{% empty %}
+<div class="alert alert-warning">{% trans "No simulator job found" %}</div>
+{%endfor%}
+{%endblock content %}
\ No newline at end of file
diff --git a/src/InSillyCloWeb/assemblies/urls.py b/src/InSillyCloWeb/assemblies/urls.py
index 8ef6778..ba9d99e 100644
--- a/src/InSillyCloWeb/assemblies/urls.py
+++ b/src/InSillyCloWeb/assemblies/urls.py
@@ -65,6 +65,7 @@ urlpatterns = [
     path('', views.index, name='assembly-simulator'),
     path('assembly/', views.AssemblyListView.as_view(), name='assembly-list'),
     path('assembly/<int:pk>/', views.AssemblyDetailView.as_view(), name='assembly-detail'),
+    path('assembly-simulator/', views.JobSimulatorListView.as_view(), name='simulator-list'),
     path('assembly-simulator/create/<str:step>/', simulator_wizard, name='simulator-create-step'),
     path('assembly-simulator/create/', simulator_wizard, name='simulator-create'),
     path('assembly-simulator/<slug:uuid>/', views.JobSimulatorResult.as_view(), name='simulator-detail'),
diff --git a/src/InSillyCloWeb/assemblies/views.py b/src/InSillyCloWeb/assemblies/views.py
index 0793b9f..32602e1 100644
--- a/src/InSillyCloWeb/assemblies/views.py
+++ b/src/InSillyCloWeb/assemblies/views.py
@@ -11,6 +11,7 @@ from django.urls import reverse_lazy
 from django.utils.translation import gettext_lazy as _
 from django.views.generic import ListView, DetailView, View, DeleteView, UpdateView
 from django.views.generic.detail import SingleObjectMixin
+from django.contrib.sessions.models import Session
 
 from . import mixins
 from assemblies import forms, models, utils
@@ -112,6 +113,22 @@ class AssemblyDeleteView(
         return super().form_valid(form)
 
 
+class JobSimulatorListView(ListView):
+    model = models.SimulatorJob
+
+    def get_queryset(self):
+        qs = super().get_queryset()
+
+        if not any([self.request.user.is_authenticated, self.request.session.session_key]):
+            return qs.none()
+
+        if self.request.user.is_authenticated:
+            qs = qs.filter(owner=self.request.user)
+        elif self.request.session.session_key:
+            qs = qs.filter(owning_session=Session.objects.get(session_key=self.request.session.session_key))
+        return qs
+
+
 class JobSimulatorResult(DetailView):
     model = models.SimulatorJob
     slug_field = 'uuid'
-- 
GitLab