22
33from evaluation_function_utils .errors import EvaluationException
44
5+ from ..evaluate import evaluation_function # type: ignore
56from . import healthcheck as health
67from . import parse , validate
78from .utils import JsonType , Response
89from .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-
1811try :
1912 from ..preview import preview_function # type: ignore
2013except 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
2620class CaseWarning (TypedDict , total = False ):
21+ """Dictionary for reporting issues when testing cases"""
22+
2723 case : int
2824 message : str
2925 detail : str
3026
3127
3228class 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
3836def 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
6570def 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
107113def 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(
139161def 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(
157192def 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
0 commit comments