Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
88f7607
Improve IsolationMethodAdmin. (#404)
emilte Jul 24, 2025
cab0801
Fix order by sample status (#421)
omfj Jul 24, 2025
f55f57b
Improve dashboard overflow (#429)
omfj Jul 25, 2025
6861767
Refactor SafeRedirectMixin to remove repetitive code (#419)
mortenlyn Jul 25, 2025
55f6603
Add filtering for sample status (#425)
mortenlyn Jul 25, 2025
a7ea30f
423 remove is seen and is prioritized from table in a order (#432)
aastabk Jul 25, 2025
0eef7dc
If an order is started (has genlab ids) it cannot be deleted by resea…
aastabk Jul 25, 2025
dc940d7
When all samples are isolated/analysed, the order is set to completed…
aastabk Jul 25, 2025
cbd2e39
Use tuples. (#418)
emilte Jul 25, 2025
c94e62c
Equipment buttons moved up. Mark as seen button has consistent stylin…
aastabk Jul 25, 2025
56123df
406 assign responsible scientist to project (#411)
aastabk Jul 25, 2025
e236cb5
Fix blank species field (#447)
mortenlyn Jul 25, 2025
f2d1175
Minor visual changes for analysis order (#446)
aastabk Jul 25, 2025
d92aac8
Processing and complete status are always seen. Added buttons to equi…
aastabk Jul 28, 2025
38eee24
Fix dashboard horizontal scroll (#460)
omfj Jul 28, 2025
c86ed2f
Add isolation method to multiple sample types (#427)
omfj Jul 28, 2025
a74cbdf
Status logic (#450)
aastabk Jul 28, 2025
bfa57cb
Update SafeRedirectMixin (#440)
mortenlyn Jul 28, 2025
384dcf8
Refactor logic to choose when analysis_orders are shown in the CSV an…
mortenlyn Jul 28, 2025
285ccaf
Remove status transition buttons for delivered and processing orders …
mortenlyn Jul 28, 2025
1d6bbf6
Add FlagField serializer for boolean representation of sample status …
mortenlyn Jul 28, 2025
cafdce4
Changed to "Clear all" as filter text. Samples page also can clear fi…
aastabk Jul 28, 2025
abed102
Change name on downloaded csv file (#457)
mortenlyn Jul 28, 2025
d820698
Add AnalysisMarkerAutocomplete and filter for markers in AnalysisOrde…
mortenlyn Jul 28, 2025
c09790a
462 refactor sample status filtering 2 (#468)
aastabk Jul 28, 2025
c8990f2
Remove prioritization feature on samples (#471)
mortenlyn Jul 28, 2025
6232fbc
Add markers to staff samples view (#428)
omfj Jul 28, 2025
a87bdf0
Update logic for analysisorder (#472)
aastabk Jul 28, 2025
581caf0
Remove custom order on dashboard (#475)
omfj Jul 28, 2025
98491f2
Fixed error in mark as seen. Adding svg for exclaimation mark. (#481)
aastabk Jul 29, 2025
2089bd8
Assign staff multiselect (#300)
omfj Jul 29, 2025
f93a424
Add project filtering and activation features (#480)
mortenlyn Jul 29, 2025
7bd4856
Style dashboard table (#483)
omfj Jul 29, 2025
8d5386e
Use format_html (#479)
omfj Jul 29, 2025
7537884
Add static choices support and hide statuses by default in filters (#…
mortenlyn Jul 29, 2025
8e5fa72
Remove genetic from table column name (#487)
omfj Jul 29, 2025
e775b6d
Set order to processing when updating sample status (#485)
omfj Jul 29, 2025
bd0a936
Major visual changes (#488)
aastabk Jul 29, 2025
9714027
Update column order complete sheet csv for terrestrisk (#490)
mortenlyn Jul 29, 2025
ca528c0
Fix N+1 query by prefetching order (#491)
mortenlyn Jul 29, 2025
ea9e461
Prefetch isolation method to fix N+1 query (#492)
mortenlyn Jul 29, 2025
684a41d
Filterset for EquipmentOrder
aastabk Jul 29, 2025
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
2 changes: 1 addition & 1 deletion src/capps/core/templates/core/partials/navigation.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% load i18n %}

<header
class="sticky top-0 z-999 flex w-full bg-white drop-shadow-1 dark:bg-boxdark dark:drop-shadow-none"
class="sticky top-0 z-50 flex w-full bg-white drop-shadow-1 dark:bg-boxdark dark:drop-shadow-none"
>
<div
class="flex flex-grow items-center justify-between px-4 py-4 shadow-2 md:px-6 2xl:px-11"
Expand Down
22 changes: 21 additions & 1 deletion src/capps/core/templatetags/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ def render(field: Any, instance: Model) -> tuple:
return None, None


IGNORED_FIELDS = ["tagged_items"]
IGNORED_FIELDS = [
"tagged_items",
"is_seen",
"is_prioritized",
"responsible_staff",
]
IGNORED_FIELDS_STAFF = ["tagged_items"]


@register.filter
Expand All @@ -45,3 +51,17 @@ def get_fields(instance: Model, fields: str | None = None) -> Any:
and field.name not in IGNORED_FIELDS
),
)


@register.filter
def get_fields_staff(instance: Model, fields: str | None = None) -> Any:
return filter(
lambda x: x[0],
(
render(field, instance)
for field in instance._meta.get_fields()
if (not fields or field.name in fields.split(" "))
and not isinstance(field, TaggableManager)
and field.name not in IGNORED_FIELDS_STAFF
),
)
2 changes: 1 addition & 1 deletion src/capps/users/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class UserAdminCreationForm(admin_forms.UserCreationForm):

class Meta(admin_forms.UserCreationForm.Meta): # type: ignore[name-defined]
model = User
fields = ["email"]
fields = ("email",)
field_classes = {"email": EmailField}
error_messages = {
"email": {"unique": _("This email has already been taken.")},
Expand Down
6 changes: 6 additions & 0 deletions src/config/autocomplete.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from capps.users.autocomplete import StaffUserAutocomplete, UserAutocomplete
from genlab_bestilling.autocomplete import (
AnalysisMarkerAutocomplete,
AnalysisOrderAutocomplete,
AreaAutocomplete,
EquipmentAutocomplete,
Expand Down Expand Up @@ -42,4 +43,9 @@
IsolationMethodAutocomplete.as_view(),
name="isolation-method",
),
path(
"analysis-marker/",
AnalysisMarkerAutocomplete.as_view(),
name="analysis-marker",
),
]
27 changes: 26 additions & 1 deletion src/genlab_bestilling/admin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from django.contrib import admin
from django.db.models import QuerySet
from django.http import HttpRequest
from unfold.admin import ModelAdmin
from unfold.contrib.filters import admin as unfold_filters

Expand Down Expand Up @@ -540,4 +542,27 @@ class AnalysisResultAdmin(ModelAdmin):


@admin.register(IsolationMethod)
class IsolationMethodAdmin(ModelAdmin): ...
class IsolationMethodAdmin(ModelAdmin):
M = IsolationMethod

list_display = [
M.name.field.name,
"get_sample_types",
]

search_help_text = "Search for isolation method name"
search_fields = [M.name.field.name]
list_filter = [
(M.name.field.name, unfold_filters.FieldTextFilter),
(M.sample_types.field.name, unfold_filters.AutocompleteSelectMultipleFilter),
]
autocomplete_fields = [M.sample_types.field.name]
filter_horizontal = [M.sample_types.field.name]
list_filter_submit = True
list_filter_sheet = False

def get_queryset(self, request: HttpRequest) -> QuerySet[IsolationMethod]:
return super().get_queryset(request).prefetch_related("sample_types")

def get_sample_types(self, obj: IsolationMethod) -> str:
return ", ".join([sample_type.name for sample_type in obj.sample_types.all()])
14 changes: 8 additions & 6 deletions src/genlab_bestilling/api/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@
"genlab_id",
"fish_id",
"guid",
"order",
"analysis_orders",
"river",
"location.name",
"under_locality",
Expand All @@ -126,9 +128,9 @@
"rerun_date",
),
"Terrestrisk": (
"name",
"genlab_id",
"guid",
"name",
"type.name",
"species.name",
"location.name",
Expand Down Expand Up @@ -197,9 +199,9 @@
}

LABEL_CSV_FIELDS_BY_AREA = {
"Akvatisk": ("fish_id", "genlab_id", "guid"),
"Elvemusling": ("fish_id", "genlab_id", "guid"),
"Terrestrisk": ("genlab_id", "guid", "name"),
"MiljøDNA": ("genlab_id", "guid", "name", "location.name"),
"default": ("genlab_id", "guid", "name"),
"Akvatisk": ("fish_id", "genlab_id", "guid", "order"),
"Elvemusling": ("fish_id", "genlab_id", "guid", "order"),
"Terrestrisk": ("genlab_id", "guid", "name", "order"),
"MiljøDNA": ("genlab_id", "guid", "name", "location.name", "order"),
"default": ("genlab_id", "guid", "name", "order"),
}
82 changes: 43 additions & 39 deletions src/genlab_bestilling/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,27 @@ class KoncivSerializer(EnumSerializer):
class MarkerSerializer(serializers.ModelSerializer):
class Meta:
model = Marker
fields = ["name"]
fields = ("name",)


class SampleTypeSerializer(serializers.ModelSerializer):
class Meta:
model = SampleType
fields = ["id", "name"]
fields = ("id", "name")


class SpeciesSerializer(serializers.ModelSerializer):
class Meta:
model = Species
fields = ["id", "name"]
fields = ("id", "name")


class LocationSerializer(serializers.ModelSerializer):
name = serializers.SerializerMethodField()

class Meta:
model = Location
fields = ["id", "name"]
fields = ("id", "name")

def get_name(self, obj: Location) -> str:
return str(obj)
Expand All @@ -60,7 +60,7 @@ def get_name(self, obj: Location) -> str:
class LocationCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Location
fields = ["id", "name"]
fields = ("id", "name")


class SampleSerializer(serializers.ModelSerializer):
Expand All @@ -77,7 +77,7 @@ def get_has_error(self, obj: Sample) -> bool:

class Meta:
model = Sample
fields = [
fields = (
"id",
"order",
"guid",
Expand All @@ -91,7 +91,12 @@ class Meta:
"type",
"has_error",
"genlab_id",
]
)


class FlagField(serializers.Field):
def to_representation(self, value: bool) -> str:
return "x" if value else ""


class SampleCSVSerializer(serializers.ModelSerializer):
Expand All @@ -102,14 +107,14 @@ class SampleCSVSerializer(serializers.ModelSerializer):
analysis_orders = serializers.SerializerMethodField()
project = serializers.SerializerMethodField()
isolation_method = serializers.SerializerMethodField()
is_marked = serializers.SerializerMethodField()
is_plucked = serializers.SerializerMethodField()
is_isolated = serializers.SerializerMethodField()
is_marked = FlagField()
is_plucked = FlagField()
is_isolated = FlagField()
internal_note = serializers.SerializerMethodField()

class Meta:
model = Sample
fields = [
fields = (
"order",
"guid",
"name",
Expand All @@ -128,14 +133,21 @@ class Meta:
"is_plucked",
"is_isolated",
"internal_note",
]
)

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()]
if not obj.order:
return []

analysis_orders = obj.order.analysis_orders.all()
# Return all analysis order IDs as strings
# only if there is exactly one analysis order, else return empty list.
# This is to ensure no duplicate rows in staffs common sheet
if analysis_orders.count() == 1:
return [str(analysis_orders.first().id)]
return []

def get_project(self, obj: Sample) -> str:
Expand All @@ -147,33 +159,25 @@ 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_is_marked(self, obj: Sample) -> str:
return self._flag(obj.is_marked)

def get_is_plucked(self, obj: Sample) -> str:
return self._flag(obj.is_plucked)

def get_is_isolated(self, obj: Sample) -> str:
return self._flag(obj.is_isolated)

def get_internal_note(self, obj: Sample) -> str:
if obj.internal_note:
return obj.internal_note
return ""


class LabelCSVSerializer(serializers.ModelSerializer):
location = LocationSerializer(allow_null=True, required=False)

class Meta:
model = Sample
fields = [
fields = (
"genlab_id",
"guid",
"name",
"fish_id",
]
"order",
"location",
)

def get_fish_id(self, obj: Sample) -> str:
return obj.fish_id or "-"
Expand All @@ -190,7 +194,7 @@ def get_has_error(self, obj: Sample) -> bool:

class Meta:
model = Sample
fields = [
fields = (
"id",
"order",
"guid",
Expand All @@ -202,7 +206,7 @@ class Meta:
"location",
"type",
"has_error",
]
)


class SampleBulkSerializer(serializers.ModelSerializer):
Expand All @@ -219,7 +223,7 @@ class SampleBulkSerializer(serializers.ModelSerializer):

class Meta:
model = Sample
fields = [
fields = (
"order",
"species",
"year",
Expand All @@ -229,23 +233,23 @@ class Meta:
"type",
"location",
"quantity",
]
)


class SampleDeleteBulkSerializer(serializers.ModelSerializer):
class Meta:
model = Sample
fields = ["order"]
fields = ("order",)


class GenrequestSerializer(serializers.ModelSerializer):
class Meta:
model = Genrequest
fields = [
fields = (
"id",
"project",
"area",
]
)


class ExtractionSerializer(serializers.ModelSerializer):
Expand All @@ -255,7 +259,7 @@ class ExtractionSerializer(serializers.ModelSerializer):

class Meta:
model = ExtractionOrder
fields = ["id", "genrequest", "species", "sample_types", "needs_guid"]
fields = ("id", "genrequest", "species", "sample_types", "needs_guid")


class AnalysisSerializer(serializers.ModelSerializer):
Expand All @@ -264,15 +268,15 @@ class AnalysisSerializer(serializers.ModelSerializer):

class Meta:
model = AnalysisOrder
fields = ["id", "genrequest", "markers"]
fields = ("id", "genrequest", "markers")


class SampleMarkerAnalysisSerializer(serializers.ModelSerializer):
sample = SampleSerializer(read_only=True)

class Meta:
model = SampleMarkerAnalysis
fields = ["id", "order", "sample", "marker"]
fields = ("id", "order", "sample", "marker")


class SampleMarkerAnalysisBulkSerializer(serializers.ModelSerializer):
Expand All @@ -285,7 +289,7 @@ class SampleMarkerAnalysisBulkSerializer(serializers.ModelSerializer):

class Meta:
model = SampleMarkerAnalysis
fields = ["order", "samples", "markers"]
fields = ("order", "samples", "markers")


class SampleMarkerAnalysisBulkDeleteSerializer(serializers.Serializer):
Expand Down
Loading