Skip to content

Commit

Permalink
Merge pull request #101 from djnunez-aot/sort-order-for-item-types
Browse files Browse the repository at this point in the history
Add statuses to submission items
  • Loading branch information
jadmsaadaot authored Oct 8, 2024
2 parents 7c732a5 + 063c69f commit cbb694a
Show file tree
Hide file tree
Showing 13 changed files with 168 additions and 47 deletions.
58 changes: 58 additions & 0 deletions submit-api/migrations/versions/67a4a5ef2087_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from alembic import op
import sqlalchemy as sa

# Revision identifiers, used by Alembic
revision = '67a4a5ef2087'
down_revision = '9655618a231b'
branch_labels = None
depends_on = None

# Define the enums
itemstatus = sa.Enum('NEW_SUBMISSION', 'PARTIALLY_COMPLETED', 'COMPLETED', name='itemstatus')
temp_itemstatus = sa.Enum('PENDING', 'NEW_SUBMISSION', 'PARTIALLY_COMPLETED', 'COMPLETED', name='temp_itemstatus')
old_itemstatus = sa.Enum('PENDING', 'COMPLETED', name='itemstatus')

def upgrade():
# Step 1: Create the temporary enum type with 'PENDING' included
temp_itemstatus.create(op.get_bind(), checkfirst=False)

# Step 2: Alter the column to use the temporary enum type, allowing both 'PENDING' and 'NEW_SUBMISSION'
op.alter_column('items', 'status', type_=temp_itemstatus, postgresql_using='status::text::temp_itemstatus')

# Step 3: Update all instances of 'PENDING' to 'NEW_SUBMISSION' in the items table
op.execute("UPDATE items SET status = 'NEW_SUBMISSION' WHERE status = 'PENDING'")

# Step 4: Drop the old enum type
itemstatus.drop(op.get_bind(), checkfirst=False)

# Step 5: Recreate the original enum with the new values (excluding 'PENDING')
itemstatus.create(op.get_bind(), checkfirst=False)

# Step 6: Alter the column to use the new version of the original enum
op.alter_column('items', 'status', type_=itemstatus, postgresql_using='status::text::itemstatus')

# Step 7: Drop the temporary enum type after migration
temp_itemstatus.drop(op.get_bind(), checkfirst=False)


def downgrade():
# Step 1: Create the temporary enum type with the values needed for conversion, including 'PENDING'
temp_itemstatus.create(op.get_bind(), checkfirst=False)

# Step 2: Alter the column to use the temporary enum type (with 'PENDING') to allow conversion
op.alter_column('items', 'status', type_=temp_itemstatus, postgresql_using='status::text::temp_itemstatus')

# Step 3: Update any instances of 'NEW_SUBMISSION' back to 'PENDING' after confirming the column type allows it
op.execute("UPDATE items SET status = 'PENDING' WHERE status = 'NEW_SUBMISSION'")
op.execute("UPDATE items SET status = 'PENDING' WHERE status = 'PARTIALLY_COMPLETED'")
# Step 4: Drop the current itemstatus enum
itemstatus.drop(op.get_bind(), checkfirst=False)

# Step 5: Recreate the old enum type (including 'PENDING' and 'COMPLETED')
old_itemstatus.create(op.get_bind(), checkfirst=False)

# Step 6: Alter the column to use the old enum type
op.alter_column('items', 'status', type_=old_itemstatus, postgresql_using='status::text::itemstatus')

# Step 7: Drop the temporary enum type after conversion
temp_itemstatus.drop(op.get_bind(), checkfirst=False)
5 changes: 3 additions & 2 deletions submit-api/src/submit_api/models/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
class ItemStatus(enum.Enum):
"""Enum for item statuses."""

PENDING = 'PENDING'
NEW_SUBMISSION = 'NEW_SUBMISSION'
PARTIALLY_COMPLETED = 'PARTIALLY_COMPLETED'
COMPLETED = 'COMPLETED'


Expand All @@ -30,7 +31,7 @@ class Item(BaseModel):
sort_order = Column(db.Integer, nullable=True, default=0)
type = db.relationship('ItemType', foreign_keys=[type_id], lazy='joined')
status = Column(Enum(ItemStatus), nullable=False,
default=ItemStatus.PENDING)
default=ItemStatus.NEW_SUBMISSION)
submitted_on = Column(db.DateTime, nullable=True)
submitted_by = Column(db.String(255), nullable=True)
version = Column(db.Integer, nullable=False, default=1)
Expand Down
9 changes: 8 additions & 1 deletion submit-api/src/submit_api/resources/submission.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from submit_api.schemas.submission import CreateSubmissionRequestSchema, SubmissionSchema
from submit_api.services.submission import SubmissionService
from submit_api.utils.util import cors_preflight

from ..auth import auth
from .apihelper import Api as ApiHelper

Expand Down Expand Up @@ -55,6 +54,10 @@ def post(submission_item_id):
"""Create a submission."""
create_submission_data = CreateSubmissionRequestSchema().load(API.payload)
created_submission = SubmissionService.create_submission(submission_item_id, create_submission_data)
item_id = create_submission_data.get("item_id")
status = create_submission_data.get("status")
if status:
SubmissionService.update_submission_item_status(item_id, status)
return SubmissionSchema().dump(created_submission), HTTPStatus.CREATED


Expand All @@ -76,4 +79,8 @@ def patch(submission_id):
"""Edit a submission."""
edit_submission_data = CreateSubmissionRequestSchema().load(API.payload)
edited_submission = SubmissionService.edit_submission_form(submission_id, edit_submission_data)
item_id = edit_submission_data.get("item_id")
status = edit_submission_data.get("status")
if status:
SubmissionService.update_submission_item_status(item_id, status)
return SubmissionSchema().dump(edited_submission), HTTPStatus.OK
2 changes: 2 additions & 0 deletions submit-api/src/submit_api/schemas/submission.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,6 @@ class Meta: # pylint: disable=too-few-public-methods
unknown = EXCLUDE

type = fields.Str(data_key="type")
status = fields.Str(data_key="status")
data = fields.Dict(data_key="data")
item_id = fields.Int(data_key="item_id")
13 changes: 13 additions & 0 deletions submit-api/src/submit_api/services/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,16 @@ def get_item_by_id(cls, item_id):
"""Get item by id."""
item = ItemModel.find_by_id(item_id)
return item

@classmethod
def update_submission_item(cls, item_id, update_data):
"""Update submission item by id."""
submission_item = cls.get_item_by_id(item_id)
if not submission_item:
raise ValueError(f"Item with id {item_id} not found.")

for key, value in update_data.items():
setattr(submission_item, key, value)

submission_item.save()
return submission_item
13 changes: 11 additions & 2 deletions submit-api/src/submit_api/services/submission/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from submit_api.models.submission import SubmissionTypeStatus
from submit_api.services.submission.submission_creator_factory import (
DocumentSubmissionCreator, FormSubmissionCreator, SubmissionCreatorFactory)
from submit_api.services.item import ItemService


class SubmissionService:
Expand Down Expand Up @@ -36,10 +37,10 @@ def create_submission(cls, item_id, request_data):
submission_type = request_data.get("type")
if not submission_type:
raise ValueError("Submission type is required.")

submission_creator = cls.make_submission_creator(submission_type)
submission_data = request_data.get("data")
return submission_creator.create(item_id, submission_data)
submission = submission_creator.create(item_id, submission_data)
return submission

@classmethod
def get_submission_by_id_and_validate_edit(cls, submission_id):
Expand All @@ -61,3 +62,11 @@ def edit_submission_form(cls, submission_id, request):
submission.submitted_form.submission_json = request.get('data')
submission.submitted_form.save()
return submission

@classmethod
def update_submission_item_status(cls, item_id, status):
"""Update the status of the submission item."""
if status is None:
raise ValueError("Status is required.")
update_data = {"status": status}
ItemService.update_submission_item(item_id, update_data)
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,7 @@ export default function SubmissionItemTableRow({
<StyledTableCell align="right"></StyledTableCell>
<StyledTableCell align="right"></StyledTableCell>
<StyledTableCell align="right">
<SubmissionStatusChip
status={SUBMISSION_STATUS.NEW_SUBMISSION.value}
/>
<SubmissionStatusChip status={status} />
</StyledTableCell>
<StyledTableCell align="right">
<Unless condition={status === SUBMISSION_STATUS.SUBMITTED.value}>
Expand Down
11 changes: 8 additions & 3 deletions submit-web/src/components/Submission/SubmissionStatusChip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ type StyleProps = {
sx: Record<string, string | number>;
label: string;
};

const statusStyles: Record<SubmissionStatus, StyleProps> = {
NEW_SUBMISSION: {
sx: {
borderRadius: 1,
border: `2px solid ${EAOColors.DecisionDark}`,
background: EAOColors.DecisionLight,
height: "24px",
},
label: "New Submission",
},
Expand All @@ -21,15 +23,17 @@ const statusStyles: Record<SubmissionStatus, StyleProps> = {
borderRadius: 1,
border: `2px solid ${BCDesignTokens.supportBorderColorSuccess}`,
background: BCDesignTokens.supportSurfaceColorSuccess,
height: "24px",
},
label: "Completed",
},
PARTIALLY_COMPLETE: {
label: "Partially Complete",
PARTIALLY_COMPLETED: {
label: "Partially Completed",
sx: {
borderRadius: 1,
border: `2px solid ${BCDesignTokens.supportBorderColorWarning}`,
background: BCDesignTokens.supportSurfaceColorWarning,
height: "24px",
},
},
SUBMITTED: {
Expand All @@ -38,6 +42,7 @@ const statusStyles: Record<SubmissionStatus, StyleProps> = {
borderRadius: 1,
border: `2px solid ${BCDesignTokens.themeBlue100}`,
background: BCDesignTokens.themeBlue20,
height: "24px",
},
},
};
Expand All @@ -47,7 +52,7 @@ export default function SubmissionStatusChip({
}: {
status: SubmissionStatus;
}) {
const style = statusStyles[status];
const style = statusStyles[status] || statusStyles.NEW_SUBMISSION;

return (
<Chip
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ import { useDocumentUploadStore } from "@/store/documentUploadStore";
import ControlledTextField from "@/components/Shared/controlled/ControlledTextField";
import { DocumentUploadSection } from "./DocumentUploadSection";
import { YesNoRadioOptions } from "@/components/Shared/YesNoRadioOptions";
import { SUBMISSION_TYPE } from "@/models/Submission";
import {
SUBMISSION_STATUS,
SUBMISSION_TYPE,
SubmissionStatus,
} from "@/models/Submission";
import CloseIcon from "@mui/icons-material/Close";
import { When } from "react-if";
import { useGetSubmissionItem } from "@/hooks/api/useItems";
Expand All @@ -40,7 +44,7 @@ const consultationRecordSchema = yup.object().shape({
consultedParty: yup
.string()
.required("Please provide the name of the consulted party."),
}),
})
)
.required("Please provide at least one consulted party."),
allPartiesConsulted: yup.string().required("Please answer this question."),
Expand Down Expand Up @@ -81,39 +85,39 @@ export const ConsultationRecord = () => {
});

const formSubmission = submissionItem?.submissions?.find(
(submission) => submission.type === SUBMISSION_TYPE.FORM,
(submission) => submission.type === SUBMISSION_TYPE.FORM
);
const defaultFormValues = useMemo(() => {
if (!formSubmission?.submitted_form?.submission_json) return {};

return {
...formSubmission.submitted_form.submission_json,
allPartiesConsulted: booleanToString(
formSubmission.submitted_form.submission_json.allPartiesConsulted,
formSubmission.submitted_form.submission_json.allPartiesConsulted
),
planWasReviewed: booleanToString(
formSubmission.submitted_form.submission_json.planWasReviewed,
formSubmission.submitted_form.submission_json.planWasReviewed
),
writtenExplanationsProvidedToParties: booleanToString(
formSubmission.submitted_form.submission_json
.writtenExplanationsProvidedToParties,
.writtenExplanationsProvidedToParties
),
writtenExplanationsProvidedToCommenters: booleanToString(
formSubmission.submitted_form.submission_json
.writtenExplanationsProvidedToCommenters,
.writtenExplanationsProvidedToCommenters
),
};
}, [formSubmission]);

const documentSubmissions = submissionItem?.submissions?.filter(
(submission) => submission.type === SUBMISSION_TYPE.DOCUMENT,
(submission) => submission.type === SUBMISSION_TYPE.DOCUMENT
);
const defaultDocumentValues = useMemo(() => {
if (!documentSubmissions) return {};

return {
consultationRecords: documentSubmissions.map(
(submission) => submission.submitted_document.url,
(submission) => submission.submitted_document.url
),
};
}, [documentSubmissions]);
Expand Down Expand Up @@ -162,7 +166,14 @@ export const ConsultationRecord = () => {
formState: { errors, dirtyFields },
} = methods;

const saveSubmission = async (formData: ConsultationRecordForm) => {
const handleCompleteForm = (formData: ConsultationRecordForm) => {
saveSubmission(formData, SUBMISSION_STATUS.COMPLETED.value); // Add default status here
};

const saveSubmission = async (
formData: ConsultationRecordForm,
status: SubmissionStatus
) => {
const {
consultedParties,
allPartiesConsulted,
Expand All @@ -173,15 +184,17 @@ export const ConsultationRecord = () => {
callSaveSubmission({
data: {
type: SUBMISSION_TYPE.FORM,
status,
item_id: submissionItemId,
data: {
consultedParties,
allPartiesConsulted: stringToBoolean(allPartiesConsulted),
planWasReviewed: stringToBoolean(planWasReviewed),
writtenExplanationsProvidedToParties: stringToBoolean(
writtenExplanationsProvidedToParties,
writtenExplanationsProvidedToParties
),
writtenExplanationsProvidedToCommenters: stringToBoolean(
writtenExplanationsProvidedToCommenters,
writtenExplanationsProvidedToCommenters
),
},
},
Expand All @@ -199,7 +212,7 @@ export const ConsultationRecord = () => {
...methods.getValues(),
};

saveSubmission(formData);
saveSubmission(formData, SUBMISSION_STATUS.PARTIALLY_COMPLETED.value);
};

useEffect(() => {
Expand Down Expand Up @@ -243,7 +256,7 @@ export const ConsultationRecord = () => {
title={accountProject.project.name + " Management Plan"}
/>
<FormProvider {...methods}>
<form onSubmit={handleSubmit(saveSubmission)}>
<form onSubmit={handleSubmit(handleCompleteForm)}>
<Grid container spacing={BCDesignTokens.layoutMarginMedium}>
<Grid item xs={12}>
<Typography
Expand Down Expand Up @@ -383,7 +396,7 @@ export const ConsultationRecord = () => {
<ControlledRadioGroup name="writtenExplanationsProvidedToParties">
<YesNoRadioOptions
error={Boolean(
errors["writtenExplanationsProvidedToParties"],
errors["writtenExplanationsProvidedToParties"]
)}
/>
</ControlledRadioGroup>
Expand All @@ -401,7 +414,7 @@ export const ConsultationRecord = () => {
<ControlledRadioGroup name="writtenExplanationsProvidedToCommenters">
<YesNoRadioOptions
error={Boolean(
errors["writtenExplanationsProvidedToCommenters"],
errors["writtenExplanationsProvidedToCommenters"]
)}
/>
</ControlledRadioGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { notify } from "@/components/Shared/Snackbar/snackbarStore";
import { useEffect, useMemo } from "react";
import { useLoaderBackdrop } from "@/components/Shared/Overlays/loaderBackdropStore";
import { Navigate, useNavigate, useParams } from "@tanstack/react-router";
import { SUBMISSION_TYPE } from "@/models/Submission";
import { SUBMISSION_STATUS, SUBMISSION_TYPE } from "@/models/Submission";
import ControlledInputMask from "@/components/Shared/controlled/ControlledInputMask";
import BarTitle from "@/components/Shared/Text/BarTitle";
import { CardInnerBox } from "@/components/Projects/Project";
Expand Down Expand Up @@ -65,7 +65,7 @@ export const ContactInformation = () => {
const navigate = useNavigate();

const formSubmission = submissionItem?.submissions.find(
(submission) => submission.type === SUBMISSION_TYPE.FORM,
(submission) => submission.type === SUBMISSION_TYPE.FORM
);
const defaultValues = useMemo(() => {
if (!formSubmission?.submitted_form?.submission_json) return {};
Expand Down Expand Up @@ -103,6 +103,8 @@ export const ContactInformation = () => {
const request = {
type: SUBMISSION_TYPE.FORM,
data: formData,
status: SUBMISSION_STATUS.COMPLETED.value,
item_id: submissionItem.id,
};
saveSubmission({
data: request,
Expand Down
Loading

0 comments on commit cbb694a

Please sign in to comment.