diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4061410b..3e3c6eab 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -7,12 +7,12 @@ env:
on:
pull_request:
- branches: ['main', 'summer25']
- paths-ignore: ['docs/**']
+ branches: ["main", "summer25", "summer25-*"]
+ paths-ignore: ["docs/**"]
push:
- branches: ['main', 'summer25']
- paths-ignore: ['docs/**']
+ branches: ["main", "summer25", "summer25-*"]
+ paths-ignore: ["docs/**"]
concurrency:
group: ${{ github.head_ref || github.run_id }}
@@ -28,7 +28,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
- python-version: '3.11'
+ python-version: "3.11"
# Consider using pre-commit.ci for open source project
- name: Run pre-commit
uses: pre-commit/action@v3.0.0
diff --git a/.gitignore b/.gitignore
index b5fac182..b48164c8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -335,3 +335,6 @@ staticfiles/
# Local History for devcontainer
.devcontainer/bash_history
+
+### macOS ###
+.DS_Store
diff --git a/src/config/autocomplete.py b/src/config/autocomplete.py
index 72e981c0..9fd60470 100644
--- a/src/config/autocomplete.py
+++ b/src/config/autocomplete.py
@@ -12,6 +12,7 @@
OrderAutocomplete,
SampleTypeAutocomplete,
SpeciesAutocomplete,
+ StatusAutocomplete,
)
from nina.autocomplete import ProjectAutocomplete
@@ -20,6 +21,7 @@
path("area/", AreaAutocomplete.as_view(), name="area"),
path("species/", SpeciesAutocomplete.as_view(), name="species"),
path("sample-type/", SampleTypeAutocomplete.as_view(), name="sample-type"),
+ path("order-status/", StatusAutocomplete.as_view(), name="order-status"),
path("project/", ProjectAutocomplete.as_view(), name="project"),
path("marker/", MarkerAutocomplete.as_view(), name="marker"),
path("user/", UserAutocomplete.as_view(), name="user"),
diff --git a/src/genlab_bestilling/admin.py b/src/genlab_bestilling/admin.py
index a9a7839f..0d58dff2 100644
--- a/src/genlab_bestilling/admin.py
+++ b/src/genlab_bestilling/admin.py
@@ -19,6 +19,7 @@
Location,
LocationType,
Marker,
+ Order,
Organization,
Sample,
SampleMarkerAnalysis,
@@ -48,6 +49,13 @@ class AreaAdmin(ModelAdmin):
list_filter_sheet = False
+@admin.register(Order)
+class OrderAdmin(admin.ModelAdmin):
+ list_display = ["id", "name", "status", "created_at"]
+ list_filter = ["status"]
+ search_fields = ["id", "name"]
+
+
@admin.register(LocationType)
class LocationTypeAdmin(ModelAdmin):
search_fields = ["name"]
diff --git a/src/genlab_bestilling/api/serializers.py b/src/genlab_bestilling/api/serializers.py
index d8d285d3..e47cfb43 100644
--- a/src/genlab_bestilling/api/serializers.py
+++ b/src/genlab_bestilling/api/serializers.py
@@ -1,7 +1,3 @@
-from collections.abc import Mapping
-from typing import Any
-
-from django.forms import Field
from rest_framework import exceptions, serializers
from ..models import (
@@ -103,10 +99,15 @@ class SampleCSVSerializer(serializers.ModelSerializer):
species = SpeciesSerializer()
location = LocationSerializer(allow_null=True, required=False)
fish_id = serializers.SerializerMethodField()
+ analysis_orders = serializers.SerializerMethodField()
+ project = serializers.SerializerMethodField()
+ isolation_method = serializers.SerializerMethodField()
+ marked = serializers.SerializerMethodField()
+ plucked = serializers.SerializerMethodField()
+ isolated = serializers.SerializerMethodField()
class Meta:
model = Sample
- # Make fields as a list to enable the removal of fish_id dynamically
fields = [
"order",
"guid",
@@ -119,20 +120,43 @@ class Meta:
"notes",
"genlab_id",
"fish_id",
+ "analysis_orders",
+ "project",
+ "isolation_method",
+ "marked",
+ "plucked",
+ "isolated",
]
- def get_field_names(
- self, declared_fields: Mapping[str, Field], info: Any
- ) -> list[str]:
- field_names = super().get_field_names(declared_fields, info)
- if not self.context.get("include_fish_id", False):
- # Remove fish_id if the area is not aquatic (only relevant for aquatic area)
- field_names.remove("fish_id")
- return field_names
-
def get_fish_id(self, obj: Sample) -> str:
return obj.fish_id or "-"
+ def get_analysis_orders(self, obj: Sample) -> list[str]:
+ if obj.order and obj.order.analysis_orders.exists():
+ return [str(anl.id) for anl in obj.order.analysis_orders.all()]
+ return []
+
+ def get_project(self, obj: Sample) -> str:
+ if obj.order and obj.order.genrequest and obj.order.genrequest.project:
+ return str(obj.order.genrequest.project)
+ return ""
+
+ def get_isolation_method(self, obj: Sample) -> str:
+ method = obj.isolation_method.first()
+ return method.name if method else ""
+
+ def _flag(self, value: bool) -> str:
+ return "x" if value else ""
+
+ def get_marked(self, obj: Sample) -> str:
+ return self._flag(obj.is_marked)
+
+ def get_plucked(self, obj: Sample) -> str:
+ return self._flag(obj.is_plucked)
+
+ def get_isolated(self, obj: Sample) -> str:
+ return self._flag(obj.is_isolated)
+
class SampleUpdateSerializer(serializers.ModelSerializer):
has_error = serializers.SerializerMethodField()
diff --git a/src/genlab_bestilling/api/views.py b/src/genlab_bestilling/api/views.py
index 7f914bfc..76dac513 100644
--- a/src/genlab_bestilling/api/views.py
+++ b/src/genlab_bestilling/api/views.py
@@ -1,8 +1,8 @@
import uuid
-from typing import Any
from django.db import transaction
-from django.db.models import QuerySet
+from django.db.models import Exists, OuterRef, QuerySet
+from django.http import HttpResponse
from django.views import View
from drf_spectacular.utils import extend_schema
from rest_framework.decorators import action
@@ -33,6 +33,7 @@
Marker,
Sample,
SampleMarkerAnalysis,
+ SampleStatusAssignment,
SampleType,
Species,
)
@@ -77,6 +78,115 @@ class SampleViewset(ModelViewSet):
pagination_class = IDCursorPagination
permission_classes = [AllowSampleDraft, IsAuthenticated]
+ CSV_FIELD_LABELS: dict[str, str] = {
+ "genlab_id": "Genlab ID",
+ "fish_id": "Old Genlab ID",
+ "guid": "GUID",
+ "name": "Name",
+ "species.name": "Species",
+ "location.name": "Location",
+ "order": "EXT_order",
+ "analysis_orders": "ANL_order",
+ "pop_id": "PopID",
+ "type.name": "Sample Type",
+ "gender": "Gender",
+ "length": "Length",
+ "weight": "Weight",
+ "classification": "Classification",
+ "year": "Date",
+ "notes": "Remarks",
+ "project": "Projectnumber",
+ "isolation_method": "Isolation Method",
+ "qiagen_number": "Qiagen#",
+ "marked": "Marked",
+ "plucked": "Plucked",
+ "isolated": "Isolated",
+ "station": "Station",
+ "placement_in_fridge": "Placement in fridge",
+ "delivered_to_lab": "Delivered to lab",
+ }
+
+ # NOTE: This can be modified to include more fields based on species or area.
+ CSV_FIELDS_BY_AREA: dict[str, list[str]] = {
+ "Akvatisk": [
+ "genlab_id",
+ "fish_id",
+ "guid",
+ "order",
+ "analysis_orders",
+ "location.name",
+ "pop_id",
+ "name",
+ "species.name",
+ "gender",
+ "length",
+ "weight",
+ "classification",
+ "year",
+ "notes",
+ "project",
+ "type.name",
+ "isolation_method",
+ "qiagen_number",
+ "marked",
+ "plucked",
+ "isolated",
+ ],
+ "Elvemusling": [
+ "genlab_id",
+ "fish_id",
+ "guid",
+ "location.name",
+ "year",
+ "name",
+ "station",
+ "type.name",
+ "length",
+ "notes",
+ "isolation_method",
+ "qiagen_number",
+ "placement_in_fridge",
+ "marked",
+ "plucked",
+ "isolated",
+ ],
+ "Terrestrisk": [
+ "genlab_id",
+ "guid",
+ "name",
+ "type.name",
+ "species.name",
+ "location.name",
+ "delivered_to_lab",
+ "order",
+ "analysis_orders",
+ "notes",
+ "marked",
+ "plucked",
+ "isolated",
+ "isolation_method",
+ "qiagen_number",
+ ],
+ # Same as "Terrestrisk" for now, can be modified later if needed.
+ "default": [
+ "genlab_id",
+ "guid",
+ "name",
+ "type.name",
+ "species.name",
+ "location.name",
+ "delivered_to_lab",
+ "order",
+ "analysis_orders",
+ "notes",
+ "marked",
+ "plucked",
+ "isolated",
+ "isolation_method",
+ "qiagen_number",
+ ],
+ }
+
def get_queryset(self) -> QuerySet:
return (
super()
@@ -89,7 +199,24 @@ def get_queryset(self) -> QuerySet:
"order__genrequest__area",
"location",
)
- .order_by("id")
+ .annotate(
+ is_marked=Exists(
+ SampleStatusAssignment.objects.filter(
+ sample=OuterRef("pk"), status="marked"
+ )
+ ),
+ is_plucked=Exists(
+ SampleStatusAssignment.objects.filter(
+ sample=OuterRef("pk"), status="plucked"
+ )
+ ),
+ is_isolated=Exists(
+ SampleStatusAssignment.objects.filter(
+ sample=OuterRef("pk"), status="isolated"
+ )
+ ),
+ )
+ .order_by("genlab_id", "type")
)
def get_serializer_class(self) -> type[BaseSerializer]:
@@ -99,21 +226,78 @@ def get_serializer_class(self) -> type[BaseSerializer]:
return SampleCSVSerializer
return super().get_serializer_class()
- def get_serializer_context(self, *args, **kwargs) -> dict[str, Any]:
- context = super().get_serializer_context(*args, **kwargs)
- queryset = self.filter_queryset(self.get_queryset())
- is_aquatic = queryset.filter(order__genrequest__area__name="Akvatisk").exists()
- context["include_fish_id"] = is_aquatic
- return context
+ def get_area_name(self, queryset: QuerySet) -> str:
+ return (
+ queryset.values_list("order__genrequest__area__name", flat=True).first()
+ or "default"
+ )
+
+ # NOTE: If the headers differ from species to species, we can add more headers
+ # to the CSV_FIELDS_BY_AREA dict, and then use the species name to determine
+ # which headers to use.
+ def get_csv_fields_and_labels(
+ self, area_name: str, queryset: QuerySet
+ ) -> tuple[list[str], list[str]]:
+ get_fields = area_name
+ if area_name == "Akvatisk":
+ species = queryset.values_list("species__name", flat=True).distinct()
+ if species.first() == "Elvemusling":
+ get_fields = "Elvemusling"
+
+ fields = self.CSV_FIELDS_BY_AREA.get(
+ get_fields, self.CSV_FIELDS_BY_AREA["default"]
+ )
+ labels = [self.CSV_FIELD_LABELS.get(f, f) for f in fields]
+ return fields, labels
+
+ # Helper function to get nested values from a dict using dotted notation.
+ def get_nested(self, obj: dict, dotted: str) -> str | None:
+ for part in dotted.split("."):
+ obj = obj.get(part) if isinstance(obj, dict) else None
+ return obj
+
+ def build_csv_data(
+ self, serialized_data: list[dict], fields: list[str]
+ ) -> list[dict[str, str]]:
+ return [
+ {
+ self.CSV_FIELD_LABELS[f]: (
+ ", ".join(v)
+ if isinstance(v := self.get_nested(item, f), list)
+ else v or ""
+ )
+ for f in fields
+ }
+ for item in serialized_data
+ ]
@action(
- methods=["GET"], url_path="csv", detail=False, renderer_classes=[CSVRenderer]
+ methods=["GET"],
+ url_path="csv",
+ detail=False,
+ renderer_classes=[CSVRenderer],
)
- def csv(self, request: Request) -> Response:
+ def csv(self, request: Request) -> HttpResponse:
queryset = self.filter_queryset(self.get_queryset())
- serializer = self.get_serializer(queryset, many=True)
- return Response(
- serializer.data,
+ area_name = self.get_area_name(queryset)
+
+ serializer = self.get_serializer(
+ queryset,
+ many=True,
+ )
+
+ fields, headers = self.get_csv_fields_and_labels(area_name, queryset)
+ data = self.build_csv_data(serializer.data, fields)
+
+ csv_data = CSVRenderer().render(
+ data,
+ media_type="text/csv",
+ renderer_context={"header": headers},
+ )
+
+ return HttpResponse(
+ csv_data,
+ content_type="text/csv; charset=utf-8",
headers={"Content-Disposition": "attachment; filename=samples.csv"},
)
diff --git a/src/genlab_bestilling/autocomplete.py b/src/genlab_bestilling/autocomplete.py
index 120d2cd3..2a3bdb63 100644
--- a/src/genlab_bestilling/autocomplete.py
+++ b/src/genlab_bestilling/autocomplete.py
@@ -1,4 +1,5 @@
from dal import autocomplete
+from django.http import HttpRequest, JsonResponse
from .models import (
AnalysisOrder,
@@ -18,6 +19,17 @@ class AreaAutocomplete(autocomplete.Select2QuerySetView):
model = Area
+class StatusAutocomplete(autocomplete.Select2QuerySetView):
+ def get(self, request: "HttpRequest", *args, **kwargs) -> JsonResponse:
+ term = request.GET.get("q", "").lower()
+ results = [
+ {"id": choice[0], "text": choice[1]}
+ for choice in Order.OrderStatus.choices
+ if term in choice[1].lower()
+ ]
+ return JsonResponse({"results": results})
+
+
class SpeciesAutocomplete(autocomplete.Select2QuerySetView):
model = Species
diff --git a/src/genlab_bestilling/migrations/0027_alter_isolationmethod_name.py b/src/genlab_bestilling/migrations/0027_alter_isolationmethod_name.py
new file mode 100644
index 00000000..80790b0a
--- /dev/null
+++ b/src/genlab_bestilling/migrations/0027_alter_isolationmethod_name.py
@@ -0,0 +1,17 @@
+# Generated by Django 5.2.3 on 2025-07-15 07:00
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("genlab_bestilling", "0026_alter_samplestatusassignment_status_and_more"),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name="isolationmethod",
+ name="name",
+ field=models.CharField(max_length=255),
+ ),
+ ]
diff --git a/src/genlab_bestilling/models.py b/src/genlab_bestilling/models.py
index 8211e20f..47f646c3 100644
--- a/src/genlab_bestilling/models.py
+++ b/src/genlab_bestilling/models.py
@@ -495,14 +495,6 @@ def confirm_order(self, persist: bool = True) -> None:
if persist:
super().confirm_order()
- def order_manually_checked(self) -> None:
- """
- Set the order as checked by the lab staff, generate a genlab id
- """
- self.internal_status = self.Status.CHECKED
- self.status = self.OrderStatus.PROCESSING
- self.save(update_fields=["internal_status", "status"])
-
@transaction.atomic
def order_selected_checked(
self,
@@ -827,7 +819,7 @@ class Meta:
class IsolationMethod(models.Model):
- name = models.CharField(max_length=255, unique=True)
+ name = models.CharField(max_length=255, unique=False)
species = models.ForeignKey(
f"{an}.Species",
on_delete=models.CASCADE,
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/analysisorder_detail.html b/src/genlab_bestilling/templates/genlab_bestilling/analysisorder_detail.html
index a23995de..11812109 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/analysisorder_detail.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/analysisorder_detail.html
@@ -33,20 +33,20 @@
Samples to analyze
-
back
+
Back
{% if object.status == 'draft' %}
-
Edit Order
+
Edit Order
{% if not object.from_order %}
-
Edit Samples
+
Edit Samples
{% endif %}
-
Summary Samples
+
Summary Samples
{% url 'genrequest-order-confirm' genrequest_id=object.genrequest_id pk=object.id as confirm_order_url %}
{% url 'genrequest-order-clone' genrequest_id=object.genrequest_id pk=object.id as clone_order_url %}
- {% action-button action=confirm_order_url class="bg-secondary text-white" submit_text="Deliver order" csrf_token=csrf_token %}
- {% action-button action=clone_order_url class="bg-secondary text-white" submit_text="Clone Order" csrf_token=csrf_token %}
-
Delete
+ {% action-button action=confirm_order_url class="btn custom_order_button" submit_text="Deliver order" csrf_token=csrf_token %}
+ {% action-button action=clone_order_url class="btn custom_order_button" submit_text="Clone Order" csrf_token=csrf_token %}
+
Delete
{% elif object.status == object.OrderStatus.DELIVERED %}
-
Samples
+
Samples
{% endif %}
{% endblock %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/analysisorder_filter.html b/src/genlab_bestilling/templates/genlab_bestilling/analysisorder_filter.html
index 23259bf5..9dc763bb 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/analysisorder_filter.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/analysisorder_filter.html
@@ -6,8 +6,8 @@
{% block page-inner %}
{% endblock page-inner %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/analysisorder_form.html b/src/genlab_bestilling/templates/genlab_bestilling/analysisorder_form.html
index 8379e70f..c201072b 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/analysisorder_form.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/analysisorder_form.html
@@ -6,9 +6,9 @@ {% if object.id %}{{ object }}{% else %}Create {{ view
{% formset endpoint=request.path csrf_token=csrf_token form=form %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/base_filter.html b/src/genlab_bestilling/templates/genlab_bestilling/base_filter.html
index 0f2c0924..e27e6f3b 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/base_filter.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/base_filter.html
@@ -9,8 +9,8 @@ {% block page-title %}{% endblock page-title %}
{% render_table table %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/equipmentorder_detail.html b/src/genlab_bestilling/templates/genlab_bestilling/equipmentorder_detail.html
index c4f3c066..77f9fbaf 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/equipmentorder_detail.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/equipmentorder_detail.html
@@ -12,7 +12,7 @@
Order {{ object }}
{% object-detail object=object %}
@@ -35,13 +35,13 @@ Requested Equipment
{% if object.status == 'draft' %}
-
Edit
-
Edit requested equipment
+
Edit
+
Edit requested equipment
{% url 'genrequest-order-confirm' genrequest_id=object.genrequest_id pk=object.id as confirm_order_url %}
- {% action-button action=confirm_order_url class="bg-secondary text-white" submit_text="Deliver order" csrf_token=csrf_token %}
-
Delete
+ {% action-button action=confirm_order_url class="btn custom_order_button" submit_text="Deliver order" csrf_token=csrf_token %}
+
Delete
{% endif %}
{% url 'genrequest-order-clone' genrequest_id=object.genrequest_id pk=object.id as clone_order_url %}
- {% action-button action=clone_order_url class="bg-secondary text-white" submit_text="Clone Order" csrf_token=csrf_token %}
+ {% action-button action=clone_order_url class="btn custom_order_button" submit_text="Clone Order" csrf_token=csrf_token %}
{% endblock %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/equipmentorder_filter.html b/src/genlab_bestilling/templates/genlab_bestilling/equipmentorder_filter.html
index 9b328e39..d0dad657 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/equipmentorder_filter.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/equipmentorder_filter.html
@@ -6,8 +6,8 @@
{% block page-inner %}
{% endblock page-inner %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/equipmentorder_form.html b/src/genlab_bestilling/templates/genlab_bestilling/equipmentorder_form.html
index 0b9e874f..cc0cc260 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/equipmentorder_form.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/equipmentorder_form.html
@@ -5,9 +5,9 @@
{% if object.id %}{{ object }}{% else %}Create {{ view.model|verbose_name }}{% endif %}
{% formset endpoint=request.path csrf_token=csrf_token form=form %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/equipmentorderquantity_form.html b/src/genlab_bestilling/templates/genlab_bestilling/equipmentorderquantity_form.html
index e9f38114..2593b8b5 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/equipmentorderquantity_form.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/equipmentorderquantity_form.html
@@ -4,7 +4,7 @@
{% block content %}
{% if object.id %}{{ object }}{% else %}Create {{ view.model|verbose_name }}{% endif %}
{% formset endpoint=request.path csrf_token=csrf_token form_collection=form_collection %}
{% endblock %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/extractionorder_detail.html b/src/genlab_bestilling/templates/genlab_bestilling/extractionorder_detail.html
index ae3a5a89..0342d567 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/extractionorder_detail.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/extractionorder_detail.html
@@ -16,30 +16,33 @@ Order {{ object }}
- {% object-detail object=object %}
-
-
- Delivered Samples
-
-
Uploaded {{ object.samples.count }} samples
-
-
-
back
+
Back
{% if object.status == 'draft' %}
-
Edit Order
-
Edit Samples
- {% url 'genrequest-order-confirm' genrequest_id=object.genrequest_id pk=object.id as confirm_order_url %}
- {% action-button action=confirm_order_url class="bg-secondary text-white" submit_text="Deliver order" csrf_token=csrf_token %}
-
Delete
+
Edit Order
+
Edit Samples
+
+ {% url 'genrequest-order-confirm' genrequest_id=object.genrequest_id pk=object.id as confirm_order_url %}
+ {% action-button action=confirm_order_url class="btn custom_order_button" submit_text="Deliver order" csrf_token=csrf_token %}
+
+
Delete
{% endif %}
-
Samples
+
+
Samples
{% if object.status != 'draft' %}
-
Analyze these samples
+
Analyze these samples
{% endif %}
{% url 'genrequest-order-clone' genrequest_id=object.genrequest_id pk=object.id as clone_order_url %}
- {% action-button action=clone_order_url class="bg-secondary text-white" submit_text="Clone Order" csrf_token=csrf_token %}
+ {% action-button action=clone_order_url class="btn custom_order_button" submit_text="Clone Order" csrf_token=csrf_token %}
+
+
+ {% object-detail object=object %}
+
+
+ Delivered Samples
+
+
Uploaded {{ object.samples.count }} samples
{% endblock %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/extractionorder_filter.html b/src/genlab_bestilling/templates/genlab_bestilling/extractionorder_filter.html
index 6668d719..b0676d9b 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/extractionorder_filter.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/extractionorder_filter.html
@@ -6,8 +6,8 @@
{% block page-inner %}
{% endblock page-inner %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/extractionorder_form.html b/src/genlab_bestilling/templates/genlab_bestilling/extractionorder_form.html
index 15e837a9..1f96a6e8 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/extractionorder_form.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/extractionorder_form.html
@@ -6,9 +6,9 @@ {% if object.id %}{{ object }}{% else %}Create {{ view
{% formset endpoint=request.path csrf_token=csrf_token form=form %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/genrequest_confirm_delete.html b/src/genlab_bestilling/templates/genlab_bestilling/genrequest_confirm_delete.html
index e57a4a4f..b3c0b53b 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/genrequest_confirm_delete.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/genrequest_confirm_delete.html
@@ -5,14 +5,14 @@
{% block content %}
Delete request {{ object }}?
Are you sure you want to delete this request?
{% endblock %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/genrequest_detail.html b/src/genlab_bestilling/templates/genlab_bestilling/genrequest_detail.html
index 191aefc5..a24452f8 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/genrequest_detail.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/genrequest_detail.html
@@ -15,49 +15,49 @@ {{ object.project_id }} - {{ object.name|d
{% endif %}
-
- {% object-detail object=object %}
+
+
+ {% object-detail object=object %}
{% endblock %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/genrequest_filter.html b/src/genlab_bestilling/templates/genlab_bestilling/genrequest_filter.html
index 9f64aa39..49382992 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/genrequest_filter.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/genrequest_filter.html
@@ -5,6 +5,6 @@
{% block page-title %}Genetic Project{% endblock page-title %}
{% block page-inner %}
{% endblock page-inner %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/genrequest_form.html b/src/genlab_bestilling/templates/genlab_bestilling/genrequest_form.html
index 2ad667d3..d06e6743 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/genrequest_form.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/genrequest_form.html
@@ -5,10 +5,10 @@
{% if object.id %}{{ object }}{% else %}Create {{ view.model|verbose_name }}{% endif %}
{% formset endpoint=request.path csrf_token=csrf_token form=form %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/order_confirm_delete.html b/src/genlab_bestilling/templates/genlab_bestilling/order_confirm_delete.html
index 8f762754..76eea90c 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/order_confirm_delete.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/order_confirm_delete.html
@@ -5,14 +5,14 @@
{% block content %}
Delete Order {{ object }}?
Are you sure you want to delete this order?
{% endblock %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/order_filter.html b/src/genlab_bestilling/templates/genlab_bestilling/order_filter.html
index 5e32f0dd..80b5169a 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/order_filter.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/order_filter.html
@@ -6,10 +6,10 @@
{% block page-inner %}
{% endblock page-inner %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/sample_list.html b/src/genlab_bestilling/templates/genlab_bestilling/sample_list.html
index dee3ee67..1ebbb60d 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/sample_list.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/sample_list.html
@@ -6,16 +6,16 @@
Samples for Extraction #{{ view.kwargs.pk }}
{% render_table table %}
-
back to order
+
Back to order
{% if extraction.status == 'draft' %}
{% url 'genrequest-order-confirm' genrequest_id=view.kwargs.genrequest_id pk=view.kwargs.pk as confirm_order_url %}
- {% action-button action=confirm_order_url class="bg-secondary text-white" submit_text="Deliver order" csrf_token=csrf_token %}
-
edit samples
+ {% action-button action=confirm_order_url class="btn custom_order_button" submit_text="Deliver order" csrf_token=csrf_token %}
+
Edit samples
{% endif %}
{% if extraction.status != 'draft' %}
-
Analyze these samples
+
Analyze these samples
{% endif %}
-
Download CSV
+
Download CSV
{% endblock %}
diff --git a/src/genlab_bestilling/templates/genlab_bestilling/samplemarkeranalysis_list.html b/src/genlab_bestilling/templates/genlab_bestilling/samplemarkeranalysis_list.html
index ede4ff29..5b81034a 100644
--- a/src/genlab_bestilling/templates/genlab_bestilling/samplemarkeranalysis_list.html
+++ b/src/genlab_bestilling/templates/genlab_bestilling/samplemarkeranalysis_list.html
@@ -13,12 +13,12 @@
{{ analysis }} - Samples
{% render_table table %}
-
back to order
+
Back to order
{% if analysis.status == 'draft' %}
{% url 'genrequest-order-confirm' genrequest_id=view.kwargs.genrequest_id pk=view.kwargs.pk as confirm_order_url %}
{% action-button action=confirm_order_url class="bg-secondary text-white" submit_text="Deliver order" csrf_token=csrf_token %}
{% if not analysis.from_order %}
-
edit samples
+
Edit samples
{% endif %}
{% endif %}
diff --git a/src/nina/templates/nina/project_detail.html b/src/nina/templates/nina/project_detail.html
index 6e5f3acc..bd38e40d 100644
--- a/src/nina/templates/nina/project_detail.html
+++ b/src/nina/templates/nina/project_detail.html
@@ -14,6 +14,28 @@
{{ object }}
{% endif %}
+
+
{% object-detail object=object %}
@@ -22,27 +44,5 @@
Members
{% render_table table %}
-
-
{% endblock %}
diff --git a/src/nina/templates/nina/project_form.html b/src/nina/templates/nina/project_form.html
index 90d3d1f2..55da8b8d 100644
--- a/src/nina/templates/nina/project_form.html
+++ b/src/nina/templates/nina/project_form.html
@@ -9,6 +9,6 @@ {% if object.id %}{{ object }}{% else %}Create {{ view
{% render_form form "tailwind" %}
- Submit
+ Submit
{% endblock %}
diff --git a/src/nina/templates/nina/projectmembership_form.html b/src/nina/templates/nina/projectmembership_form.html
index 792de137..fbdfb9e1 100644
--- a/src/nina/templates/nina/projectmembership_form.html
+++ b/src/nina/templates/nina/projectmembership_form.html
@@ -4,7 +4,7 @@
{% block content %}
{{ object }}
{% formset endpoint=request.path csrf_token=csrf_token form_collection=form_collection %}
{% endblock %}
diff --git a/src/nina/templates/nina/projectmembership_list.html b/src/nina/templates/nina/projectmembership_list.html
index 4a0790c1..d626c8fe 100644
--- a/src/nina/templates/nina/projectmembership_list.html
+++ b/src/nina/templates/nina/projectmembership_list.html
@@ -4,7 +4,7 @@
{% block content %}
NINA Projects you are involved in
{% render_table table %}
diff --git a/src/staff/filters.py b/src/staff/filters.py
index 067247b7..271d13a8 100644
--- a/src/staff/filters.py
+++ b/src/staff/filters.py
@@ -2,18 +2,56 @@
import django_filters as filters
from dal import autocomplete
+from django import forms
from django.db.models import QuerySet
from django.http import HttpRequest
+from django_filters import CharFilter
from genlab_bestilling.models import (
AnalysisOrder,
+ ExtractionOrder,
ExtractionPlate,
+ Order,
Sample,
SampleMarkerAnalysis,
)
class AnalysisOrderFilter(filters.FilterSet):
+ class Meta:
+ model = AnalysisOrder
+ fields = ["id", "status", "genrequest__area"]
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ self.filters["id"].field.label = "Order ID"
+ self.filters["id"].field.widget = forms.TextInput(
+ attrs={
+ "class": "bg-white border border-gray-300 rounded-lg py-2 px-4 w-full text-gray-700", # noqa: E501
+ "placeholder": "Enter Order ID",
+ }
+ )
+
+ self.filters["status"].field.label = "Order Status"
+ self.filters["status"].field.choices = Order.OrderStatus.choices
+ self.filters["status"].field.widget = autocomplete.ListSelect2(
+ url="autocomplete:order-status",
+ attrs={
+ "class": "w-full",
+ },
+ )
+
+ self.filters["genrequest__area"].field.label = "Area"
+ self.filters["genrequest__area"].field.widget = autocomplete.ModelSelect2(
+ url="autocomplete:area",
+ attrs={
+ "class": "w-full",
+ },
+ )
+
+
+class OrderFilter(filters.FilterSet):
def __init__(
self,
data: dict[str, Any] | None = None,
@@ -31,7 +69,7 @@ def __init__(
)
class Meta:
- model = AnalysisOrder
+ model = ExtractionOrder
fields = [
"id",
"status",
@@ -41,6 +79,24 @@ class Meta:
class OrderSampleFilter(filters.FilterSet):
+ genlab_id = CharFilter(
+ label="GenlabID",
+ widget=forms.TextInput(
+ attrs={
+ "placeholder": "Type here",
+ }
+ ),
+ )
+
+ name = CharFilter(
+ label="Name",
+ widget=forms.TextInput(
+ attrs={
+ "placeholder": "Type here",
+ }
+ ),
+ )
+
def __init__(
self,
data: dict[str, Any] | None = None,
@@ -56,23 +112,20 @@ def __init__(
self.filters["type"].extra["widget"] = autocomplete.ModelSelect2(
url="autocomplete:sample-type"
)
- self.filters["location"].extra["widget"] = autocomplete.ModelSelect2(
- url="autocomplete:location"
- )
class Meta:
model = Sample
fields = [
# "order",
- "guid",
- "name",
+ # "guid",
"genlab_id",
+ "name",
"species",
"type",
- "year",
- "location",
- "pop_id",
- "type",
+ # "year",
+ # "location",
+ # "pop_id",
+ # "type",
# "desired_extractions",
]
diff --git a/src/staff/mixins.py b/src/staff/mixins.py
new file mode 100644
index 00000000..95b5b918
--- /dev/null
+++ b/src/staff/mixins.py
@@ -0,0 +1,52 @@
+import django_tables2 as tables
+from django.utils.safestring import mark_safe
+
+from genlab_bestilling.models import (
+ AnalysisOrder,
+ EquipmentOrder,
+ ExtractionOrder,
+ Order,
+)
+
+
+def render_status_helper(value: Order.OrderStatus) -> str:
+ status_colors = {
+ Order.OrderStatus.PROCESSING: "bg-yellow-100 text-yellow-800",
+ Order.OrderStatus.COMPLETED: "bg-green-100 text-green-800",
+ Order.OrderStatus.DELIVERED: "bg-red-100 text-red-800",
+ }
+ status_text = {
+ Order.OrderStatus.PROCESSING: "Processing",
+ Order.OrderStatus.COMPLETED: "Completed",
+ Order.OrderStatus.DELIVERED: "Not started",
+ Order.OrderStatus.DRAFT: "Draft",
+ }
+ color_class = status_colors.get(value, "bg-gray-100 text-gray-800")
+ status_text = status_text.get(value, "Unknown")
+ return mark_safe( # noqa: S308
+ f'{status_text} ' # noqa: E501
+ )
+
+
+class StatusMixinTable(tables.Table):
+ status = tables.Column(
+ orderable=False,
+ verbose_name="Status",
+ )
+
+ def render_status(self, value: Order.OrderStatus, record: Order) -> str:
+ return render_status_helper(record.status)
+
+
+class StaffIDMixinTable(tables.Table):
+ id = tables.Column(
+ orderable=False,
+ empty_values=(),
+ )
+
+ def render_id(
+ self, record: ExtractionOrder | AnalysisOrder | EquipmentOrder
+ ) -> str:
+ url = record.get_absolute_staff_url()
+
+ return mark_safe(f'{record} ') # noqa: S308
diff --git a/src/staff/tables.py b/src/staff/tables.py
index a9ce9222..c70373de 100644
--- a/src/staff/tables.py
+++ b/src/staff/tables.py
@@ -1,3 +1,5 @@
+from dataclasses import dataclass
+from datetime import datetime
from typing import Any
import django_tables2 as tables
@@ -16,6 +18,28 @@
)
from nina.models import Project
+from .mixins import StaffIDMixinTable, StatusMixinTable, render_status_helper
+
+
+@dataclass
+class CombinedOrder:
+ extraction_order: ExtractionOrder
+ analysis_order: AnalysisOrder | None = None
+ priority: Order.OrderPriority = Order.OrderPriority.NORMAL
+ assigned_staff: list[str] | None = None
+
+ def status(self) -> Order.OrderStatus:
+ """Returns the lowest status of the extraction and analysis orders."""
+ order = Order.STATUS_ORDER
+ if self.analysis_order:
+ return min(
+ self.extraction_order.status,
+ self.analysis_order.status,
+ key=order.index,
+ )
+
+ return self.extraction_order.status
+
class ProjectTable(tables.Table):
number = tables.Column(
@@ -31,107 +55,148 @@ class Meta:
class OrderTable(tables.Table):
- id = tables.Column(
- linkify=True,
+ priority = tables.Column(
orderable=False,
- empty_values=(),
+ verbose_name="Priority",
+ accessor="priority",
)
- is_urgent = tables.Column(
- orderable=True,
- visible=True,
- verbose_name="",
+ def render_priority(self, value: Order.OrderPriority) -> str:
+ if value == Order.OrderPriority.URGENT:
+ return mark_safe(
+ ' ' # noqa: E501
+ )
+ return ""
+
+ def get_extraction_link(record: CombinedOrder) -> str | None:
+ return record.extraction_order.get_absolute_staff_url()
+
+ ext_id = tables.Column(
+ linkify=get_extraction_link,
+ orderable=False,
+ empty_values=(),
+ verbose_name="EXT ID",
+ accessor="extraction_order",
)
- status = tables.Column(
- verbose_name="Status",
+ def render_ext_id(self, record: CombinedOrder) -> str:
+ return str(record.extraction_order)
+
+ def get_analysis_link(record: CombinedOrder) -> str | None:
+ if record.analysis_order is not None:
+ return record.analysis_order.get_absolute_staff_url()
+ return None
+
+ anl_id = tables.Column(
+ linkify=get_analysis_link,
orderable=False,
+ empty_values=(),
+ verbose_name="ANL ID",
+ accessor="analysis_order",
)
- is_seen = tables.Column(
+ def render_anl_id(self, record: CombinedOrder) -> str:
+ if record.analysis_order:
+ return str(record.analysis_order)
+ return "-"
+
+ ext_status = tables.Column(
+ accessor="extraction_order__status",
+ verbose_name="EXT status",
orderable=False,
- visible=True,
- verbose_name="",
)
- class Meta:
- fields = [
- "name",
- "status",
- "genrequest",
- "genrequest__name",
- "genrequest__project",
- "genrequest__area",
- "genrequest__samples_owner",
- "created_at",
- "last_modified_at",
- "is_urgent",
- "is_seen",
- ]
- sequence = ("is_seen", "is_urgent", "status", "id", "name")
- empty_text = "No Orders"
- order_by = ("-is_urgent", "last_modified_at", "created_at")
+ def render_ext_status(self, value: Order.OrderStatus, record: CombinedOrder) -> str:
+ return render_status_helper(value)
- def render_id(self, record: Any) -> str:
- return str(record)
+ anl_status = tables.Column(
+ verbose_name="ANL status",
+ orderable=False,
+ accessor="analysis_order__status",
+ empty_values=(),
+ )
- def render_is_urgent(self, value: bool) -> str:
- html_exclaimation_mark = (
- " "
- )
- if value:
- return mark_safe(html_exclaimation_mark) # noqa: S308
- else:
- return ""
+ def render_anl_status(self, value: Order.OrderStatus, record: CombinedOrder) -> str:
+ if record.analysis_order:
+ return render_status_helper(value)
+ return "-"
- def render_is_seen(self, value: bool) -> str:
- if not value:
- return mark_safe(
- ' '
- )
- return ""
+ area = tables.Column(
+ accessor="extraction_order__genrequest__area__name",
+ verbose_name="Area",
+ orderable=False,
+ )
+ description = tables.Column(
+ accessor="extraction_order__genrequest__name",
+ verbose_name="Description",
+ orderable=False,
+ )
-class AnalysisOrderTable(OrderTable):
- id = tables.Column(
- linkify=("staff:order-analysis-detail", {"pk": tables.A("id")}),
+ total_samples_extraction = tables.Column(
+ accessor="extraction_order__sample_count",
+ verbose_name="Total samples EXT",
orderable=False,
- empty_values=(),
)
- class Meta(OrderTable.Meta):
- model = AnalysisOrder
- fields = OrderTable.Meta.fields + ["return_samples"]
+ total_samples_analysis = tables.Column(
+ accessor="analysis_order__sample_count",
+ verbose_name="Total samples ANL",
+ orderable=False,
+ )
+ samples_isolated = tables.Column(
+ accessor="extraction_order__sample_isolated_count",
+ verbose_name="Samples isolated",
+ orderable=False,
+ )
-class ExtractionOrderTable(OrderTable):
- id = tables.Column(
- linkify=("staff:order-extraction-detail", {"pk": tables.A("id")}),
+ markers = tables.ManyToManyColumn(
+ accessor="extraction_order__genrequest__markers",
+ transform=lambda x: x.name,
+ )
+
+ assigned_staff = tables.Column(
+ accessor="assigned_staff",
+ verbose_name="Assigned staff",
orderable=False,
empty_values=(),
)
- sample_count = tables.Column(
- accessor="sample_count",
- verbose_name="Sample Count",
+ def render_assigned_staff(self, value: list[str] | None) -> str:
+ if value:
+ return ", ".join(value)
+ return "-"
+
+ delivery_date = tables.Column(
+ accessor="analysis_order__expected_delivery_date",
+ verbose_name="Delivery date",
orderable=False,
+ empty_values=(),
)
- class Meta(OrderTable.Meta):
- model = ExtractionOrder
- fields = OrderTable.Meta.fields + [
- "species",
- "sample_types",
- "internal_status",
- "needs_guid",
- "return_samples",
- "pre_isolated",
- ]
- sequence = OrderTable.Meta.sequence + ("sample_count",)
+ def render_delivery_date(self, value: datetime) -> str:
+ if value:
+ return value.strftime("%d/%m/%Y")
+ return "-"
- def render_sample_count(self, record: Any) -> str:
- return record.sample_count or "0"
+ class Meta:
+ fields = [
+ "priority",
+ "ext_id",
+ "anl_id",
+ "ext_status",
+ "anl_status",
+ "area",
+ "description",
+ "total_samples_extraction",
+ "total_samples_analysis",
+ "samples_isolated",
+ "markers",
+ "assigned_staff",
+ "delivery_date",
+ ]
+ empty_text = "No Orders"
class EquipmentOrderTable(OrderTable):
@@ -284,10 +349,15 @@ class Meta:
"isolation_method",
]
+ def render_checked(self, record: Any) -> str:
+ return mark_safe( # noqa: S308
+ f' ' # noqa: E501
+ )
+
class OrderExtractionSampleTable(SampleBaseTable):
class Meta(SampleBaseTable.Meta):
- fields = SampleBaseTable.Meta.fields
+ exclude = ("pop_id", "location")
class OrderAnalysisSampleTable(tables.Table):
@@ -346,45 +416,14 @@ class Meta:
empty_text = "No Plates"
-class StatusMixinTable(tables.Table):
- status = tables.Column(
- orderable=False,
- verbose_name="Status",
- )
-
- def render_status(self, value: Order.OrderStatus, record: Order) -> str:
- status_colors = {
- "Processing": "bg-yellow-100 text-yellow-800",
- "Completed": "bg-green-100 text-green-800",
- "Delivered": "bg-red-100 text-red-800",
- }
- status_text = {
- "Processing": "Processing",
- "Completed": "Completed",
- "Delivered": "Not started",
- }
- color_class = status_colors.get(value, "bg-gray-100 text-gray-800")
- status_text = status_text.get(value, "Unknown")
- return mark_safe( # noqa: S308
- f'{status_text} ' # noqa: E501
- )
-
-
-class StaffIDMixinTable(tables.Table):
- id = tables.Column(
+class UrgentOrderTable(StaffIDMixinTable, StatusMixinTable):
+ priority = tables.TemplateColumn(
orderable=False,
- empty_values=(),
+ verbose_name="Priority",
+ accessor="priority",
+ template_name="staff/components/priority_column.html",
)
- def render_id(
- self, record: ExtractionOrder | AnalysisOrder | EquipmentOrder
- ) -> str:
- url = record.get_absolute_staff_url()
-
- return mark_safe(f'{record} ') # noqa: S308
-
-
-class UrgentOrderTable(StaffIDMixinTable, StatusMixinTable):
description = tables.Column(
accessor="genrequest__name",
verbose_name="Description",
@@ -392,29 +431,28 @@ class UrgentOrderTable(StaffIDMixinTable, StatusMixinTable):
)
delivery_date = tables.Column(
- accessor="genrequest__expected_samples_delivery_date",
verbose_name="Delivery date",
orderable=False,
)
- def render_delivery_date(self, value: Any) -> str:
+ def render_delivery_date(self, value: datetime | None) -> str:
if value:
return value.strftime("%d/%m/%Y")
return "-"
class Meta:
model = Order
- fields = ["id", "description", "delivery_date", "status"]
+ fields = ["priority", "id", "description", "delivery_date", "status"]
empty_text = "No urgent orders"
template_name = "django_tables2/tailwind_inner.html"
class NewUnseenOrderTable(StaffIDMixinTable):
seen = tables.TemplateColumn(
+ verbose_name="",
orderable=False,
- verbose_name="Seen",
- template_name="staff/components/seen_column.html",
empty_values=(),
+ template_name="staff/components/seen_column.html",
)
description = tables.Column(
@@ -424,12 +462,11 @@ class NewUnseenOrderTable(StaffIDMixinTable):
)
delivery_date = tables.Column(
- accessor="genrequest__expected_samples_delivery_date",
verbose_name="Delivery date",
orderable=False,
)
- def render_delivery_date(self, value: Any) -> str:
+ def render_delivery_date(self, value: datetime | None) -> str:
if value:
return value.strftime("%d/%m/%Y")
return "-"
@@ -471,12 +508,11 @@ class NewSeenOrderTable(StaffIDMixinTable):
)
delivery_date = tables.Column(
- accessor="genrequest__expected_samples_delivery_date",
verbose_name="Delivery date",
orderable=False,
)
- def render_delivery_date(self, value: Any) -> str:
+ def render_delivery_date(self, value: datetime | None) -> str:
if value:
return value.strftime("%d/%m/%Y")
return "-"
@@ -530,9 +566,9 @@ class AssignedOrderTable(StatusMixinTable, StaffIDMixinTable):
orderable=False,
)
- def render_samples_completed(self, value: int) -> str:
+ def render_samples_completed(self, value: int, record: Order) -> str:
if value > 0:
- return "- / " + str(value)
+ return str(record.isolated_sample_count) + " / " + str(value)
return "-"
class Meta:
@@ -557,12 +593,11 @@ class DraftOrderTable(StaffIDMixinTable):
)
delivery_date = tables.Column(
- accessor="genrequest__expected_samples_delivery_date",
verbose_name="Delivery date",
orderable=False,
)
- def render_delivery_date(self, value: Any) -> str:
+ def render_delivery_date(self, value: datetime | None) -> str:
if value:
return value.strftime("%d/%m/%Y")
return "-"
diff --git a/src/staff/templates/staff/analysisorder_detail.html b/src/staff/templates/staff/analysisorder_detail.html
index fc2ae045..bf6df0b0 100644
--- a/src/staff/templates/staff/analysisorder_detail.html
+++ b/src/staff/templates/staff/analysisorder_detail.html
@@ -1,5 +1,6 @@
{% extends "staff/base.html" %}
{% load i18n %}
+{% load order_tags %}
{% block content %}
@@ -7,35 +8,43 @@ Order {{ object }}
-
back
-
Samples
+
Back
+
Samples
{% if extraction_order %}
-
Go to {{ extraction_order}}
+
Go to {{ extraction_order}}
{% endif %}
-
Assign staff
-
- {% if not object.is_seen %}
+ {% if object.genrequest.responsible_staff.all|is_responsible:request.user and not object.is_seen %}
{% endif %}
- {% if object.status == object.OrderStatus.DELIVERED %}
+ {% if object.status != object.OrderStatus.DRAFT %}
{% url 'staff:order-to-draft' pk=object.id as to_draft_url %}
- {% action-button action=to_draft_url class="bg-secondary text-white" submit_text="Convert to draft" csrf_token=csrf_token %}
+ {% action-button action=to_draft_url class="custom_order_button" submit_text="
Convert to draft"|safe csrf_token=csrf_token %}
{% endif %}
- {% if object.status != object.OrderStatus.DRAFT and object.next_status %}
+ {% if object.status == object.OrderStatus.DELIVERED %}
{% url 'staff:order-to-next-status' pk=object.id as to_next_status_url %}
- {% with "Set as "|add:object.next_status as btn_name %}
- {% action-button action=to_next_status_url class="bg-secondary text-white" submit_text=btn_name csrf_token=csrf_token %}
+ {% with "
Set as "|add:object.next_status as btn_name %}
+ {% action-button action=to_next_status_url class="bg-yellow-200 text-yellow-800 border border-yellow-700 hover:bg-yellow-300" submit_text=btn_name csrf_token=csrf_token %}
{% endwith %}
{% endif %}
+
+ {% if object.status == object.OrderStatus.PROCESSING %}
+ {% url 'staff:order-to-next-status' pk=object.id as to_next_status_url %}
+ {% with "
Set as "|add:object.next_status as btn_name %}
+ {% action-button action=to_next_status_url class="custom_order_button_green" submit_text=btn_name csrf_token=csrf_token %}
+ {% endwith %}
+ {% endif %}
+
+
{% fragment as table_header %}
diff --git a/src/staff/templates/staff/analysisorder_filter.html b/src/staff/templates/staff/analysisorder_filter.html
index a0ac89a4..c388eb48 100644
--- a/src/staff/templates/staff/analysisorder_filter.html
+++ b/src/staff/templates/staff/analysisorder_filter.html
@@ -10,8 +10,8 @@
{% render_table table %}
diff --git a/src/staff/templates/staff/base.html b/src/staff/templates/staff/base.html
index 80cb99de..0951c392 100644
--- a/src/staff/templates/staff/base.html
+++ b/src/staff/templates/staff/base.html
@@ -7,9 +7,8 @@ Menu
diff --git a/src/staff/templates/staff/components/seen_column.html b/src/staff/templates/staff/components/seen_column.html
index 4e318c3c..4b5deec7 100644
--- a/src/staff/templates/staff/components/seen_column.html
+++ b/src/staff/templates/staff/components/seen_column.html
@@ -1,5 +1,11 @@
+{% load order_tags %}
+
+{% if record.genrequest.responsible_staff.all|is_responsible:request.user %}
+{% endif %}
diff --git a/src/staff/templates/staff/equipmentorder_detail.html b/src/staff/templates/staff/equipmentorder_detail.html
index 7f76f07b..061dee90 100644
--- a/src/staff/templates/staff/equipmentorder_detail.html
+++ b/src/staff/templates/staff/equipmentorder_detail.html
@@ -11,7 +11,7 @@
Order {{ object }}
{% object-detail object=object %}
@@ -33,8 +33,8 @@ Requested Equipment
{% /table %}
-
back
-
Assign staff
+
Back
+
Assign staff
{% comment %}
{% if object.status == 'draft' %}
Edit
diff --git a/src/staff/templates/staff/equipmentorder_filter.html b/src/staff/templates/staff/equipmentorder_filter.html
index 434f0c13..07928ac6 100644
--- a/src/staff/templates/staff/equipmentorder_filter.html
+++ b/src/staff/templates/staff/equipmentorder_filter.html
@@ -10,8 +10,8 @@
{% render_table table %}
diff --git a/src/staff/templates/staff/extractionorder_detail.html b/src/staff/templates/staff/extractionorder_detail.html
index cf21b4c0..4bb4777c 100644
--- a/src/staff/templates/staff/extractionorder_detail.html
+++ b/src/staff/templates/staff/extractionorder_detail.html
@@ -1,5 +1,7 @@
{% extends "staff/base.html" %}
{% load i18n %}
+{% load order_tags %}
+{% load static %}
{% block content %}
@@ -7,13 +9,13 @@
Order {{ object }}
-
back
-
Samples
+
Back
+
Samples
{% if analysis_orders|length > 1 %}
Go to Analysis Order
@@ -32,36 +34,33 @@ Order {{ object }}
{% elif analysis_orders|length == 1 %}
-
Go to {{ analysis_orders.first}}
+
Go to {{ analysis_orders.first}}
{% endif %}
-
Assign staff
- {% if not object.is_seen %}
+ {% if object.genrequest.responsible_staff.all|is_responsible:request.user and not object.is_seen %}
{% endif %}
- {% if object.status == object.OrderStatus.DELIVERED %}
- {% url 'staff:order-manually-checked' pk=object.id as confirm_check_url %}
- {% action-button action=confirm_check_url class="bg-secondary text-white" submit_text="Confirm - Order checked" csrf_token=csrf_token %}
- {% endif %}
-
{% if object.status != object.OrderStatus.DRAFT %}
{% url 'staff:order-to-draft' pk=object.id as to_draft_url %}
- {% action-button action=to_draft_url class="bg-secondary text-white" submit_text="Convert to draft" csrf_token=csrf_token %}
+ {% action-button action=to_draft_url class="custom_order_button" submit_text="
Convert to draft"|safe csrf_token=csrf_token %}
{% endif %}
- {% if object.next_status %}
+ {% if object.status == object.OrderStatus.DELIVERED and object.internal_status == "checked" %}
{% url 'staff:order-to-next-status' pk=object.id as to_next_status_url %}
- {% with "Set as "|add:object.next_status as btn_name %}
- {% action-button action=to_next_status_url class="bg-secondary text-white" submit_text=btn_name csrf_token=csrf_token %}
+ {% with "
Set as "|add:object.next_status as btn_name %}
+ {% action-button action=to_next_status_url class="bg-yellow-200 text-yellow-800 border border-yellow-700 hover:bg-yellow-300" submit_text=btn_name csrf_token=csrf_token %}
{% endwith %}
{% endif %}
+
{% fragment as table_header %}
{% #table-cell header=True %}GUID{% /table-cell %}
diff --git a/src/staff/templates/staff/extractionorder_filter.html b/src/staff/templates/staff/extractionorder_filter.html
index e2c1e8d9..b2327ebe 100644
--- a/src/staff/templates/staff/extractionorder_filter.html
+++ b/src/staff/templates/staff/extractionorder_filter.html
@@ -3,15 +3,15 @@
{% load render_table from django_tables2 %}
{% block page-title %}
-Extraction Orders
+Orders
{% endblock page-title %}
{% block page-inner %}
{% render_table table %}
diff --git a/src/staff/templates/staff/extractionplate_detail.html b/src/staff/templates/staff/extractionplate_detail.html
index 07c5904c..e688e864 100644
--- a/src/staff/templates/staff/extractionplate_detail.html
+++ b/src/staff/templates/staff/extractionplate_detail.html
@@ -10,6 +10,6 @@ Plate {{ object }}
{% object-detail object=object %}
{% endblock %}
diff --git a/src/staff/templates/staff/extractionplate_filter.html b/src/staff/templates/staff/extractionplate_filter.html
index 26e046dd..4463ae68 100644
--- a/src/staff/templates/staff/extractionplate_filter.html
+++ b/src/staff/templates/staff/extractionplate_filter.html
@@ -8,14 +8,14 @@
{% block page-inner %}
{% render_table table %}
diff --git a/src/staff/templates/staff/extractionplate_form.html b/src/staff/templates/staff/extractionplate_form.html
index 83ce49f6..8274f222 100644
--- a/src/staff/templates/staff/extractionplate_form.html
+++ b/src/staff/templates/staff/extractionplate_form.html
@@ -5,15 +5,15 @@
{% if object.id %}{{ object }}{% else %}Create {{ view.model|verbose_name }}{% endif %}
diff --git a/src/staff/templates/staff/order_staff_edit.html b/src/staff/templates/staff/order_staff_edit.html
index efeab57b..9ea3ef7d 100644
--- a/src/staff/templates/staff/order_staff_edit.html
+++ b/src/staff/templates/staff/order_staff_edit.html
@@ -17,9 +17,9 @@ Manage Responsible Staff - {{ object }}
@@ -43,7 +43,7 @@ Assign Staff to Order
- Update staff assignment
+ Update staff assignment
diff --git a/src/staff/templates/staff/project_detail.html b/src/staff/templates/staff/project_detail.html
index 1a6b19e3..4aa8eb2a 100644
--- a/src/staff/templates/staff/project_detail.html
+++ b/src/staff/templates/staff/project_detail.html
@@ -7,7 +7,7 @@ Project {{ object }}
-
back
+
Back
{% url 'staff:projects-verify' pk=object.pk as verify_url %}
{% if object.verified_at is null %}
{% action-button action=verify_url class="btn-secondary text-white" submit_text="Mark as verified" csrf_token=csrf_token %}
diff --git a/src/staff/templates/staff/project_filter.html b/src/staff/templates/staff/project_filter.html
index b0434b4c..eb62d2fe 100644
--- a/src/staff/templates/staff/project_filter.html
+++ b/src/staff/templates/staff/project_filter.html
@@ -10,8 +10,8 @@
{% render_table table %}
diff --git a/src/staff/templates/staff/sample_detail.html b/src/staff/templates/staff/sample_detail.html
index 6b6c7c77..477ba47d 100644
--- a/src/staff/templates/staff/sample_detail.html
+++ b/src/staff/templates/staff/sample_detail.html
@@ -10,6 +10,6 @@
Sample {{ object }}
{% object-detail object=object %}
{% endblock %}
diff --git a/src/staff/templates/staff/sample_filter.html b/src/staff/templates/staff/sample_filter.html
index 430cc1b9..4d085073 100644
--- a/src/staff/templates/staff/sample_filter.html
+++ b/src/staff/templates/staff/sample_filter.html
@@ -11,32 +11,47 @@
Samples
{% endif %}
- {% if order %}
-
- {{ order.filled_genlab_count }} / {{ order.samples.count }} Genlabs generated
-
- {% endif %}
{% endblock page-title %}
{% block page-inner %}
{% if order %}
-
+
+
Back
+
Lab
+ {% if order.status == order.OrderStatus.DELIVERED and order.internal_status == "checked" %}
+ {% url 'staff:order-to-next-status' pk=order.pk as to_next_status_url %}
+ {% action-button action=to_next_status_url class="bg-secondary text-white" submit_text="Set as processing" csrf_token=csrf_token %}
+ {% endif %}
+
-
+
+
{% endcomment %}
+