Skip to content

Commit

Permalink
allow bulk delete of samples
Browse files Browse the repository at this point in the history
  • Loading branch information
nicokant committed Nov 20, 2024
1 parent eb05572 commit 7c05252
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 15 deletions.
40 changes: 37 additions & 3 deletions src/frontend/src/samples/components/Table.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ import { AxiosError } from "axios";
import NumberCellInput from "./Cell/NumberCellInput";
// import MultiSelectCell from "./Cell/MultiSelectCell";

function askConfirm(fn) {
return () => confirm('Are you really sure?') && fn()
}

async function getSamples({ pageParam }) {
const url = pageParam || `/api/samples/?order=${config.order}`;
const response = await client.get(url);
Expand Down Expand Up @@ -150,6 +154,8 @@ const COLUMNS = [
].filter((_) => _);

function handleError(e) {
console.error(e)

if (e instanceof AxiosError) {
e.response.data.errors.forEach((err) => {
toast.error(err.detail);
Expand Down Expand Up @@ -240,6 +246,27 @@ export default function Table() {
onError: handleError,
});

const mutateDeleteAllRows = useMutation({
mutationFn: () => {
return client.post(`/api/analysis-order/${config.order}/delete-samples/`);
},
onSuccess: () => {
toast.success("Samples deleted!");
queryClient.invalidateQueries({ queryKey: ["samples"] });
},
onError: (e) => {
console.error(e)
toast.error("There was an error!");
},
});

const pendingState = isLoading ||
isFetching ||
mutateDeleteAllRows.isPending ||
mutateConfirm.isPending ||
deleteRow.isPending ||
updateCell.isPending

const table = useReactTable({
data: flatData,
columns: COLUMNS,
Expand Down Expand Up @@ -348,8 +375,7 @@ export default function Table() {
<div className="flex justify-center py-5">
{(isLoading ||
isFetching ||
updateCell.isPending ||
deleteRow.isPending) && (
pendingState) && (
<i className="fas fa-spinner fa-spin text-lg" />
)}
</div>
Expand All @@ -376,11 +402,19 @@ export default function Table() {
<button
className="btn bg-secondary disabled:opacity-70 text-white"
onClick={mutateConfirm.mutate}
disabled={mutateConfirm.isPending}
disabled={pendingState}
>
Validate samples{" "}
{mutateConfirm.isPending && <i className="fas fa-spinner fa-spin" />}
</button>
<button
className="btn bg-red-500 disabled:opacity-70 text-white"
onClick={askConfirm(mutateDeleteAllRows.mutate)}
disabled={pendingState}
>
Delete all samples{" "}
{mutateDeleteAllRows.isPending && <i className="fas fa-spinner fa-spin" />}
</button>
</div>
</>
);
Expand Down
51 changes: 39 additions & 12 deletions src/genlab_bestilling/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,23 +42,28 @@ def has_object_permission(self, request, view, obj):


class SampleViewset(ModelViewSet):
queryset = (
Sample.objects.all()
.select_related(
"type",
"species",
"order",
"order__genrequest",
"order__genrequest__area",
"location",
)
.order_by("id")
)
queryset = Sample.objects.all()
serializer_class = SampleSerializer
filterset_class = SampleFilter
pagination_class = IDCursorPagination
permission_classes = [AllowSampleDraft, IsAuthenticated]

def get_queryset(self):
return (
super()
.get_queryset()
.filter_allowed(self.request.user)
.select_related(
"type",
"species",
"order",
"order__genrequest",
"order__genrequest__area",
"location",
)
.order_by("id")
)

def get_serializer_class(self):
if self.action in ["update", "partial_update"]:
return SampleUpdateSerializer
Expand All @@ -85,6 +90,8 @@ def bulk_create(self, request):
}

order = serializer.validated_data["order"]
# TODO: raise validation issue if order is not "owned"
# by a project the user is part of
samples = []
with transaction.atomic():
for i in range(qty):
Expand Down Expand Up @@ -119,6 +126,12 @@ class AnalysisOrderViewset(
queryset = AnalysisOrder.objects.all()
serializer_class = AnalysisSerializer

def get_queryset(self):
qs = super().get_queryset().filter_allowed(self.request.user)
if self.request.method not in SAFE_METHODS:
qs = qs.filter_in_draft()
return qs

@action(
methods=["POST"],
url_path="confirm",
Expand All @@ -130,6 +143,20 @@ def confirm_order(self, request, pk):
obj.confirm_order(persist=False)
return Response(self.get_serializer(obj).data)

@extend_schema(responses={200: OperationStatusSerializer})
@action(
methods=["POST"],
url_path="delete-samples",
detail=True,
permission_classes=[AllowOrderDraft],
)
def bulk_delete(self, request, pk):
obj = self.get_object()
with transaction.atomic():
obj.samples.all().delete()

return Response(data=OperationStatusSerializer({"success": True}).data)


class SampleTypeViewset(mixins.ListModelMixin, GenericViewSet):
queryset = SampleType.objects.all().order_by("name")
Expand Down

0 comments on commit 7c05252

Please sign in to comment.