diff options
-rw-r--r-- | backend/routes/forms/form.py | 70 |
1 files changed, 66 insertions, 4 deletions
diff --git a/backend/routes/forms/form.py b/backend/routes/forms/form.py index 86bbf49..9414e30 100644 --- a/backend/routes/forms/form.py +++ b/backend/routes/forms/form.py @@ -1,9 +1,10 @@ """Returns, updates or deletes a single form given an ID.""" +import enum import json.decoder import deepmerge -from pydantic import ValidationError +from pydantic import BaseModel, ValidationError from spectree.response import Response from starlette.authentication import requires from starlette.requests import Request @@ -18,6 +19,33 @@ from backend.validation import ErrorMessage, OkayResponse, api PUBLIC_FORM_FEATURES = (constants.FormFeatures.OPEN, constants.FormFeatures.DISCOVERABLE) +class SubmissionPrecheckSeverity(enum.StrEnum): + SECONDARY = "secondary" + WARNING = "warning" + DANGER = "danger" + + +class SubmissionProblem(BaseModel): + severity: SubmissionPrecheckSeverity + message: str + + +class SubmissionPrecheck(BaseModel): + problems: list[SubmissionProblem] = [] + can_submit: bool = True + + +class FormWithAncillaryData(Form): + """ + Form model with ancillary data for the form. + + This is used to return the form with additional information such as + whether the user has edit access or not. + """ + + submission_precheck: SubmissionPrecheck = SubmissionPrecheck() + + class SingleForm(Route): """ Returns, updates or deletes a single form given an ID. @@ -28,14 +56,17 @@ class SingleForm(Route): name = "form" path = "/{form_id:str}" - @api.validate(resp=Response(HTTP_200=Form, HTTP_404=ErrorMessage), tags=["forms"]) + @api.validate(resp=Response(HTTP_200=FormWithAncillaryData, HTTP_404=ErrorMessage), tags=["forms"]) async def get(self, request: Request) -> JSONResponse: """Returns single form information by ID.""" form_id = request.path_params["form_id"].lower() if form_id == AUTH_FORM.id: # Empty form for login purposes - return JSONResponse(AUTH_FORM.dict(admin=False)) + data = AUTH_FORM.dict(admin=False) + # Add in empty ancillary data + data["submission_precheck"] = SubmissionPrecheck().dict() + return JSONResponse(data) try: await discord.verify_edit_access(form_id, request) @@ -53,10 +84,41 @@ class SingleForm(Route): filters["features"] = {"$in": ["OPEN", "DISCOVERABLE"]} form = await request.state.db.forms.find_one(filters) + form = FormWithAncillaryData(**form) if not form: return JSONResponse({"error": "not_found"}, status_code=404) - return JSONResponse(Form(**form).dict(admin=admin)) + if constants.FormFeatures.OPEN.value not in form.features: + form.submission_precheck.problems.append( + SubmissionProblem( + severity=SubmissionPrecheckSeverity.DANGER, + message="This form is not open for submissions at the moment." + ) + ) + form.submission_precheck.can_submit = False + elif constants.FormFeatures.UNIQUE_RESPONDER.value in form.features: + user_id = request.user.payload["id"] if request.user.is_authenticated else None + if user_id: + existing_response = await request.state.db.responses.find_one( + {"form_id": form_id, "user.id": user_id} + ) + if existing_response: + form.submission_precheck.problems.append( + SubmissionProblem( + severity=SubmissionPrecheckSeverity.DANGER, + message="You have already submitted a response to this form." + ) + ) + form.submission_precheck.can_submit = False + else: + form.submission_precheck.problems.append( + SubmissionProblem( + severity=SubmissionPrecheckSeverity.SECONDARY, + message="You must login at the bottom of the page before submitting this form." + ) + ) + + return JSONResponse(form.dict(admin=admin)) @requires(["authenticated"]) @api.validate( |