Skip to content

Commit f96ee44

Browse files
committed
feat: edit comments in bulk approval when all are the same
1 parent e869114 commit f96ee44

7 files changed

Lines changed: 64 additions & 27 deletions

File tree

backend/application/core/api/serializers_observation.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -694,21 +694,35 @@ def validate(self, attrs: dict) -> dict:
694694

695695

696696
class ObservationLogBulkApprovalSerializer(Serializer):
697-
assessment_status = ChoiceField(choices=Assessment_Status.ASSESSMENT_STATUS_CHOICES_APPROVAL_BULK, required=False)
697+
assessment_status = ChoiceField(choices=Assessment_Status.ASSESSMENT_STATUS_CHOICES_APPROVAL, required=False)
698698
rejection_remark = CharField(max_length=255, required=False, allow_blank=True)
699+
observation_log_comment = CharField(max_length=4096, required=False, allow_blank=True)
699700
observation_logs = ListField(child=IntegerField(min_value=1), min_length=0, max_length=250, required=True)
700701

701702
def validate(self, attrs: dict) -> dict:
702-
if attrs.get("assessment_status") == Assessment_Status.ASSESSMENT_STATUS_APPROVED and attrs.get(
703-
"rejection_remark"
704-
):
703+
if attrs.get("assessment_status") in [
704+
Assessment_Status.ASSESSMENT_STATUS_APPROVED,
705+
Assessment_Status.ASSESSMENT_STATUS_APPROVED_WITH_EDITS,
706+
] and attrs.get("rejection_remark"):
705707
raise ValidationError("Remark for rejection cannot be set with approval")
706708

707709
if attrs.get("assessment_status") == Assessment_Status.ASSESSMENT_STATUS_REJECTED and not attrs.get(
708710
"rejection_remark"
709711
):
710712
raise ValidationError("Rejection needs a remark")
711713

714+
if attrs.get("assessment_status") in [
715+
Assessment_Status.ASSESSMENT_STATUS_APPROVED,
716+
Assessment_Status.ASSESSMENT_STATUS_REJECTED,
717+
]:
718+
if attrs.get("observation_log_comment"):
719+
raise ValidationError("Comment for observation Log cannot be set with approval or rejection")
720+
721+
if attrs.get("assessment_status") == Assessment_Status.ASSESSMENT_STATUS_APPROVED_WITH_EDITS and not attrs.get(
722+
"observation_log_comment"
723+
):
724+
raise ValidationError("Approval with edits needs an observation log comment")
725+
712726
return super().validate(attrs)
713727

714728

backend/application/core/api/views_observation.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ def bulk_approval(self, request: Request) -> Response:
347347
observation_logs_bulk_approval(
348348
request_serializer.validated_data.get("assessment_status"),
349349
request_serializer.validated_data.get("rejection_remark"),
350+
request_serializer.validated_data.get("observation_log_comment"),
350351
request_serializer.validated_data.get("observation_logs"),
351352
)
352353
return Response(status=HTTP_204_NO_CONTENT)

backend/application/core/services/observations_bulk_actions.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,12 @@ def _check_observations(product: Optional[Product], observation_ids: list[int])
136136
def observation_logs_bulk_approval(
137137
assessment_status: str,
138138
rejection_remark: str,
139+
observation_log_comment: Optional[str],
139140
observation_log_ids: list[int],
140141
) -> None:
141142
observation_logs = _check_observation_logs(None, observation_log_ids)
142143
for observation_log in observation_logs:
143-
assessment_approval(observation_log, assessment_status, rejection_remark, None, None, None)
144+
assessment_approval(observation_log, assessment_status, rejection_remark, observation_log_comment, None, None)
144145
set_potential_duplicate_both_ways(observation_log.observation)
145146

146147

backend/application/core/types.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,6 @@ class Assessment_Status:
8686
(ASSESSMENT_STATUS_REJECTED, ASSESSMENT_STATUS_REJECTED),
8787
]
8888

89-
ASSESSMENT_STATUS_CHOICES_APPROVAL_BULK = [
90-
(ASSESSMENT_STATUS_APPROVED, ASSESSMENT_STATUS_APPROVED),
91-
(ASSESSMENT_STATUS_REJECTED, ASSESSMENT_STATUS_REJECTED),
92-
]
93-
9489

9590
class Observation_Log_Comment:
9691
COMMENT_SET_BY_PARSER = "Set by parser"

backend/config/settings/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
DATABASES = {
7575
"default": {
7676
"ATOMIC_REQUESTS": True,
77-
"OPTIONS": {"options": "-c search_path=secobserve"},
77+
# "OPTIONS": {"options": "-c search_path=secobserve"},
7878
"ENGINE": env("DATABASE_ENGINE"),
7979
"HOST": env("DATABASE_HOST"),
8080
"PORT": env("DATABASE_PORT"),

backend/config/settings/dist.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,7 @@
44
# GENERAL
55
# ------------------------------------------------------------------------------
66
# https://docs.djangoproject.com/en/dev/ref/settings/#debug
7-
DEBUG = False
8-
# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
9-
SECRET_KEY = env(
10-
"DJANGO_SECRET_KEY",
11-
default="NxYPEF5lNGgk3yonndjSbwP77uNJxOvfKTjF5aVBqsHktNlf1wfJHHvJ8iifk32r",
12-
)
13-
# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
14-
ALLOWED_HOSTS = env("ALLOWED_HOSTS", default=["localhost", "0.0.0.0", "127.0.0.1"]) # nosec B104
15-
# This file is not used for production
7+
DEBUG = True
168

179
# MIDDLEWARE
1810
# ------------------------------------------------------------------------------

frontend/src/core/observation_logs/AssessmentBulkApproval.tsx

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import ApprovalIcon from "@mui/icons-material/Approval";
22
import { Dialog, DialogContent, DialogTitle } from "@mui/material";
3-
import { Fragment, useState } from "react";
3+
import { Fragment, useState, useRef, useEffect } from "react";
44
import { SimpleForm, useListContext, useNotify, useRefresh, useUnselectAll } from "react-admin";
55

66
import SmallButton from "../../commons/custom_fields/SmallButton";
@@ -9,29 +9,54 @@ import { ToolbarCancelSave } from "../../commons/custom_fields/ToolbarCancelSave
99
import { validate_required, validate_required_255 } from "../../commons/custom_validators";
1010
import { AutocompleteInputMedium, TextInputWide } from "../../commons/layout/themes";
1111
import { httpClient } from "../../commons/ra-data-django-rest-framework";
12-
import { ASSESSMENT_STATUS_APPROVED, ASSESSMENT_STATUS_BULK_CHOICES, ASSESSMENT_STATUS_REJECTED } from "../types";
12+
import { ASSESSMENT_STATUS_APPROVED, ASSESSMENT_STATUS_APPROVED_WITH_EDITS, ASSESSMENT_STATUS_BULK_CHOICES, ASSESSMENT_STATUS_CHOICES, ASSESSMENT_STATUS_REJECTED } from "../types";
13+
import MarkdownEdit from "../../commons/custom_fields/MarkdownEdit";
1314

1415
type AssessmentBulkApprovalProps = {
1516
storeKey: string;
1617
};
1718

1819
const AssessmentBulkApproval = ({ storeKey }: AssessmentBulkApprovalProps) => {
20+
const dialogRef = useRef<HTMLDivElement>(null);
1921
const [open, setOpen] = useState(false);
2022
const [decision, setDecision] = useState(ASSESSMENT_STATUS_APPROVED);
2123
const refresh = useRefresh();
2224
const notify = useNotify();
23-
const { selectedIds } = useListContext();
25+
const { data = [], selectedIds } = useListContext();
2426
const unselectAll = useUnselectAll("observation_logs", storeKey);
2527
const [loading, setLoading] = useState(false);
2628

29+
const selectedRecords = data.filter(record =>
30+
selectedIds.includes(record.id)
31+
);
32+
33+
const [comment, setComment] = useState("");
34+
35+
const allSame =
36+
selectedRecords.length > 0 &&
37+
selectedRecords.every(r => r.comment === selectedRecords[0].comment);
38+
39+
useEffect(() => {
40+
if (allSame) {
41+
setComment(selectedRecords[0].comment ?? "");
42+
}
43+
}, [allSame, selectedRecords[0]?.comment]);
44+
2745
const assessmentUpdate = async (data: any) => {
2846
setLoading(true);
29-
const post_data = {
47+
let post_data: Record<string, any> = {
3048
assessment_status: data.assessment_status,
3149
rejection_remark: data.rejection_remark,
3250
observation_logs: selectedIds,
3351
};
3452

53+
if (data.assessment_status === ASSESSMENT_STATUS_REJECTED) {
54+
post_data.rejection_remark = data.rejection_remark;
55+
}
56+
if (data.assessment_status === ASSESSMENT_STATUS_APPROVED_WITH_EDITS) {
57+
post_data.observation_log_comment = comment;
58+
}
59+
3560
httpClient(window.__RUNTIME_CONFIG__.API_BASE_URL + "/observation_logs/bulk_approval/", {
3661
method: "POST",
3762
body: JSON.stringify(post_data),
@@ -66,7 +91,7 @@ const AssessmentBulkApproval = ({ storeKey }: AssessmentBulkApprovalProps) => {
6691
return (
6792
<Fragment>
6893
<SmallButton title="Approval" onClick={handleOpen} icon={<ApprovalIcon />} />
69-
<Dialog open={open && !loading} onClose={handleClose}>
94+
<Dialog open={open && !loading} onClose={handleClose} maxWidth="lg">
7095
<DialogTitle sx={{ display: "flex", alignItems: "center" }}>
7196
<ApprovalIcon />
7297
&nbsp;&nbsp;Assessment approval
@@ -75,7 +100,7 @@ const AssessmentBulkApproval = ({ storeKey }: AssessmentBulkApprovalProps) => {
75100
<SimpleForm onSubmit={assessmentUpdate} toolbar={<ToolbarCancelSave onClick={handleCancel} />}>
76101
<AutocompleteInputMedium
77102
source="assessment_status"
78-
choices={ASSESSMENT_STATUS_BULK_CHOICES}
103+
choices={allSame ? ASSESSMENT_STATUS_CHOICES : ASSESSMENT_STATUS_BULK_CHOICES}
79104
validate={validate_required}
80105
label="Decision"
81106
onChange={(e) => setDecision(e)}
@@ -86,7 +111,16 @@ const AssessmentBulkApproval = ({ storeKey }: AssessmentBulkApprovalProps) => {
86111
validate={validate_required_255}
87112
label="Remark for rejection"
88113
/>
89-
)}{" "}
114+
)}
115+
{decision == ASSESSMENT_STATUS_APPROVED_WITH_EDITS && (
116+
<MarkdownEdit
117+
initialValue={comment}
118+
setValue={setComment}
119+
label="Comment of Observation Log *"
120+
overlayContainer={dialogRef.current ?? null}
121+
maxLength={4096}
122+
/>
123+
)}
90124
</SimpleForm>
91125
</DialogContent>
92126
</Dialog>

0 commit comments

Comments
 (0)