1
1
"""Returns, updates or deletes a single form given an ID."""
2
2
3
+ import enum
3
4
import json .decoder
4
5
5
6
import deepmerge
6
- from pydantic import ValidationError
7
+ from pydantic import BaseModel , ValidationError
7
8
from spectree .response import Response
8
9
from starlette .authentication import requires
9
10
from starlette .requests import Request
18
19
PUBLIC_FORM_FEATURES = (constants .FormFeatures .OPEN , constants .FormFeatures .DISCOVERABLE )
19
20
20
21
22
+ class SubmissionPrecheckSeverity (enum .StrEnum ):
23
+ SECONDARY = "secondary"
24
+ WARNING = "warning"
25
+ DANGER = "danger"
26
+
27
+
28
+ class SubmissionProblem (BaseModel ):
29
+ severity : SubmissionPrecheckSeverity
30
+ message : str
31
+
32
+
33
+ class SubmissionPrecheck (BaseModel ):
34
+ problems : list [SubmissionProblem ] = []
35
+ can_submit : bool = True
36
+
37
+
38
+ class FormWithAncillaryData (Form ):
39
+ """
40
+ Form model with ancillary data for the form.
41
+
42
+ This is used to return the form with additional information such as
43
+ whether the user has edit access or not.
44
+ """
45
+
46
+ submission_precheck : SubmissionPrecheck = SubmissionPrecheck ()
47
+
48
+
21
49
class SingleForm (Route ):
22
50
"""
23
51
Returns, updates or deletes a single form given an ID.
@@ -28,14 +56,17 @@ class SingleForm(Route):
28
56
name = "form"
29
57
path = "/{form_id:str}"
30
58
31
- @api .validate (resp = Response (HTTP_200 = Form , HTTP_404 = ErrorMessage ), tags = ["forms" ])
59
+ @api .validate (resp = Response (HTTP_200 = FormWithAncillaryData , HTTP_404 = ErrorMessage ), tags = ["forms" ])
32
60
async def get (self , request : Request ) -> JSONResponse :
33
61
"""Returns single form information by ID."""
34
62
form_id = request .path_params ["form_id" ].lower ()
35
63
36
64
if form_id == AUTH_FORM .id :
37
65
# Empty form for login purposes
38
- return JSONResponse (AUTH_FORM .dict (admin = False ))
66
+ data = AUTH_FORM .dict (admin = False )
67
+ # Add in empty ancillary data
68
+ data ["submission_precheck" ] = SubmissionPrecheck ().dict ()
69
+ return JSONResponse (data )
39
70
40
71
try :
41
72
await discord .verify_edit_access (form_id , request )
@@ -53,10 +84,41 @@ async def get(self, request: Request) -> JSONResponse:
53
84
filters ["features" ] = {"$in" : ["OPEN" , "DISCOVERABLE" ]}
54
85
55
86
form = await request .state .db .forms .find_one (filters )
87
+ form = FormWithAncillaryData (** form )
56
88
if not form :
57
89
return JSONResponse ({"error" : "not_found" }, status_code = 404 )
58
90
59
- return JSONResponse (Form (** form ).dict (admin = admin ))
91
+ if constants .FormFeatures .OPEN .value not in form .features :
92
+ form .submission_precheck .problems .append (
93
+ SubmissionProblem (
94
+ severity = SubmissionPrecheckSeverity .DANGER ,
95
+ message = "This form is not open for submissions at the moment."
96
+ )
97
+ )
98
+ form .submission_precheck .can_submit = False
99
+ elif constants .FormFeatures .UNIQUE_RESPONDER .value in form .features :
100
+ user_id = request .user .payload ["id" ] if request .user .is_authenticated else None
101
+ if user_id :
102
+ existing_response = await request .state .db .responses .find_one (
103
+ {"form_id" : form_id , "user.id" : user_id }
104
+ )
105
+ if existing_response :
106
+ form .submission_precheck .problems .append (
107
+ SubmissionProblem (
108
+ severity = SubmissionPrecheckSeverity .DANGER ,
109
+ message = "You have already submitted a response to this form."
110
+ )
111
+ )
112
+ form .submission_precheck .can_submit = False
113
+ else :
114
+ form .submission_precheck .problems .append (
115
+ SubmissionProblem (
116
+ severity = SubmissionPrecheckSeverity .SECONDARY ,
117
+ message = "You must login at the bottom of the page before submitting this form."
118
+ )
119
+ )
120
+
121
+ return JSONResponse (form .dict (admin = admin ))
60
122
61
123
@requires (["authenticated" ])
62
124
@api .validate (
0 commit comments