Skip to content

Commit fcb1763

Browse files
committed
Added commenting to package
1 parent 353b5e0 commit fcb1763

File tree

9 files changed

+181
-114
lines changed

9 files changed

+181
-114
lines changed

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ COPY handler.py ./app/
2424
COPY tests/*.py ./app/tests/
2525
COPY tools/*.py ./app/tools/
2626

27+
# Request-response-schemas repo/branch to use for validation
2728
ENV SCHEMAS_URL = https://raw.githubusercontent.com/lambda-feedback/request-response-schemas/579-adding-preview-command

handler.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@
77

88

99
def handle_command(event: JsonType, command: str) -> HandlerResponse:
10+
"""Switch case for handling different command options.
11+
12+
Args:
13+
event (JsonType): The AWS Lambda event recieved by the handler.
14+
command (str): The name of the function to invoke.
15+
16+
Returns:
17+
HandlerResponse: The response object returned by the handler.
18+
"""
19+
# No validation of the doc commands.
1020
if command in ("docs-dev", "docs"):
1121
return docs.dev()
1222

@@ -35,15 +45,14 @@ def handle_command(event: JsonType, command: str) -> HandlerResponse:
3545
return response
3646

3747

38-
def handler(event: JsonType, context: JsonType = {}) -> HandlerResponse:
39-
"""
40-
Main function invoked by AWS Lambda to handle incoming requests.
41-
---
42-
This function invokes the handler function for that particular command
43-
and returns
44-
the result. It also performs validation on the response body to make sure
45-
it follows
46-
the schema set out in the request-response-schema repo.
48+
def handler(event: JsonType, _: JsonType = {}) -> HandlerResponse:
49+
"""Main function invoked by AWS Lambda to handle incoming requests.
50+
51+
Args:
52+
event (JsonType): The AWS Lambda event received by the gateway.
53+
54+
Returns:
55+
HandlerResponse: The response to return back to the requestor.
4756
"""
4857
headers = event.get("headers", dict())
4958
command = headers.get("command", "eval")
@@ -60,6 +69,7 @@ def handler(event: JsonType, context: JsonType = {}) -> HandlerResponse:
6069
except EvaluationException as e:
6170
error = e.error_dict
6271

72+
# Catch-all for any unexpected errors.
6373
except Exception as e:
6474
error = ErrorResponse(
6575
message="An exception was raised while "

tests/commands.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import unittest
22

3-
# from ..tools import commands
3+
from ..tools import commands # noqa
44

55

66
class TestCommandsModule(unittest.TestCase):

tools/commands.py

Lines changed: 84 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,60 @@
22

33
from evaluation_function_utils.errors import EvaluationException
44

5+
from ..evaluate import evaluation_function # type: ignore
56
from . import healthcheck as health
67
from . import parse, validate
78
from .utils import JsonType, Response
89
from .validate import ReqBodyValidators
910

10-
try:
11-
from ..evaluate import evaluation_function # type: ignore
12-
except ImportError:
13-
14-
def evaluation_function(response: Any, answer: Any, params: Any) -> Dict:
15-
return {"is_correct": True}
16-
17-
1811
try:
1912
from ..preview import preview_function # type: ignore
2013
except ImportError:
2114

2215
def preview_function(response: Any, params: Any) -> Dict:
16+
"""Placeholder preview function if not already defined."""
2317
return {"preview": response}
2418

2519

2620
class CaseWarning(TypedDict, total=False):
21+
"""Dictionary for reporting issues when testing cases"""
22+
2723
case: int
2824
message: str
2925
detail: str
3026

3127

3228
class CaseResult(NamedTuple):
29+
"""Tuple used to compile results from all cases."""
30+
3331
is_correct: bool = False
3432
feedback: str = ""
3533
warning: Optional[CaseWarning] = None
3634

3735

3836
def healthcheck() -> Response:
37+
"""Run the healthcheck command for the evaluation function.
38+
39+
Returns:
40+
Response: The body of the response returned by the handler.
41+
"""
3942
result = health.healthcheck()
4043
return Response(command="healthcheck", result=result)
4144

4245

43-
def preview(event: JsonType):
44-
"""
45-
Function to create the response when commanded to preview an answer.
46-
---
47-
This function attempts to parse the request body, performs schema
48-
validation and
49-
attempts to run the evaluation function on the given parameters.
50-
51-
If any of these fail, a message is returned and an error field is
52-
passed if more
53-
information can be provided.
46+
def preview(event: JsonType) -> Response:
47+
"""Run the preview command for the evaluation function.
48+
49+
Note:
50+
The body of the event is validated against the preview schema
51+
before running the preview function.
52+
53+
Args:
54+
event (JsonType): The dictionary received by the gateway. Must
55+
include a body field which may be a JSON string or a dictionary.
56+
57+
Returns:
58+
Response: The result given the response and params in the body.
5459
"""
5560
body = parse.body(event)
5661

@@ -63,26 +68,29 @@ def preview(event: JsonType):
6368

6469

6570
def evaluate(event: JsonType) -> Response:
66-
"""
67-
Function to create the response when commanded to evaluate an answer.
68-
---
69-
This function attempts to parse the request body, performs schema
70-
validation and
71-
attempts to run the evaluation function on the given parameters.
72-
73-
If any of these fail, a message is returned and an error field is
74-
passed if more
75-
information can be provided.
76-
"""
71+
"""Run the evaluation command for the evaluation function.
72+
73+
Note:
74+
The body of the event is validated against the eval schema
75+
before running the evaluation function.
76+
77+
If cases are included in the params, this function checks for
78+
matching answers and returns the specified feedback.
79+
80+
Args:
81+
event (JsonType): The dictionary received by the gateway. Must
82+
include a body field which may be a JSON string or a dictionary.
7783
84+
Returns:
85+
Response: The result given the response and params in the body.
86+
"""
7887
body = parse.body(event)
7988
validate.body(body, ReqBodyValidators.EVALUATION)
8089

8190
params = body.get("params", {})
8291
result = evaluation_function(body["response"], body["answer"], params)
8392

8493
if "cases" in params and len(params["cases"]) > 0:
85-
# Determine what feedback to provide based on cases
8694
match, warnings = get_case_feedback(
8795
body["response"], params, params["cases"]
8896
)
@@ -99,14 +107,28 @@ def evaluate(event: JsonType) -> Response:
99107
if "mark" in match:
100108
result["is_correct"] = bool(int(match["mark"]))
101109

102-
# Add warnings out output if any were encountered
103-
104110
return Response(command="eval", result=result)
105111

106112

107113
def get_case_feedback(
108114
response: Any, params: Dict, cases: List[Dict]
109115
) -> Tuple[Optional[Dict], List[CaseWarning]]:
116+
"""Get the feedback case that matches an answer.
117+
118+
Args:
119+
response (Any): The student response.
120+
params (Dict): The evaluation function params.
121+
cases (List[Dict]): The list of potential cases to check against.
122+
Must contain a feedback and answer field. May optionally contain
123+
a mark field to override the is_correct response from the
124+
evaluation function.
125+
126+
Returns:
127+
Tuple[Optional[Dict], List[CaseWarning]]: The case that matched the
128+
student's response (null if no match was found) and a list of
129+
issues encountered when evaluating each case against the student's
130+
response.
131+
"""
110132
matches, feedback, warnings = evaluate_all_cases(response, params, cases)
111133

112134
if not matches:
@@ -139,6 +161,19 @@ def get_case_feedback(
139161
def evaluate_all_cases(
140162
response: Any, params: Dict, cases: List[Dict]
141163
) -> Tuple[List[int], List[str], List[CaseWarning]]:
164+
"""Loops through all cases and compiles the results.
165+
166+
Args:
167+
response (Any): The student's response.
168+
params (Dict): The params of the evaluation function.
169+
cases (List[Dict]): A list of cases to check against.
170+
171+
Returns:
172+
Tuple[List[int], List[str], List[CaseWarning]]: Returns a list of
173+
indices of cases that match a student's response, a list of feedback
174+
strings from each case, and a list of issues encountered when
175+
evaluating cases against the student's response.
176+
"""
142177
matches, feedback, warnings = [], [], []
143178

144179
for index, case in enumerate(cases):
@@ -157,7 +192,21 @@ def evaluate_all_cases(
157192
def evaluate_case(
158193
response: Any, params: Dict, case: Dict, index: int
159194
) -> CaseResult:
160-
# Validate the case block has an answer and feedback
195+
"""Evaluate a single case against a student's response.
196+
197+
Args:
198+
response (Any): The student's response.
199+
params (Dict): The params of the evaluation function.
200+
case (Dict): The case to evaluate. Must contain a feedback and answer
201+
field. May optionally contain a mark field to override the is_correct
202+
result.
203+
index (int): The index of the case in the list of cases (as an id).
204+
205+
Returns:
206+
CaseResult: The result of the case, including whether evaluated as
207+
correct, the feedback associated and the warning encountered
208+
(if any, else None).
209+
"""
161210
if "answer" not in case or "feedback" not in case:
162211
warning = CaseWarning(
163212
case=index, message="Missing answer/feedback field"
@@ -168,7 +217,6 @@ def evaluate_case(
168217
# Merge current evaluation params with any specified in case
169218
case_params = case.get("params", {})
170219

171-
# Run the evaluation function based on this case's answer
172220
try:
173221
result = evaluation_function(
174222
response, case["answer"], {**params, **case_params}
@@ -179,6 +227,7 @@ def evaluate_case(
179227
feedback=result.get("feedback", ""),
180228
)
181229

230+
# Catch exceptions and save as a warning.
182231
except EvaluationException as e:
183232
warning = CaseWarning(case=index, **e.error_dict)
184233

tools/docs.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@
55

66

77
def send_file(filepath: str) -> DocsResponse:
8-
"""
9-
Fetch and return a file given by filepath
10-
"""
8+
"""Create a response object for receiving a file.
9+
10+
Args:
11+
filepath (str): The path of the file to send.
1112
13+
Returns:
14+
DocsResponse: The response object the handler should return.
15+
"""
1216
if not os.path.isfile(filepath):
1317
return DocsResponse(
1418
statusCode=200,
@@ -17,7 +21,6 @@ def send_file(filepath: str) -> DocsResponse:
1721
isBase64Encoded=False,
1822
)
1923

20-
# Read file
2124
with open(filepath, "rb") as file:
2225
docs_file = file.read()
2326

@@ -32,16 +35,10 @@ def send_file(filepath: str) -> DocsResponse:
3235

3336

3437
def user() -> DocsResponse:
35-
"""
36-
Return the user (teacher) documentation for this function
37-
"""
38-
38+
"""Return the user (teacher) documentation for this function."""
3939
return send_file("app/docs/user.md")
4040

4141

4242
def dev() -> DocsResponse:
43-
"""
44-
Return the developer (teacher) documentation for this function
45-
"""
46-
43+
"""Return the developer (teacher) documentation for this function."""
4744
return send_file("app/docs/dev.md")

0 commit comments

Comments
 (0)