Result checks¶
-
allow_error
(state)¶ Allow submission to pass, even if it originally caused a database error.
This function has been renamed.
Use
Ex().allow_errors()
in your SCT if the intent of the exercise is to generate an error.
-
check_all_columns
(state, allow_extra=True, too_many_cols_msg=None, expand_msg=None)¶ Zoom in on the columns that are specified by the solution
Behind the scenes, this is using
check_column()
for every column that is in the solution query result. Afterwards, it’s selecting only these columns from the student query result and stores them in a child state that is returned, so you can usehas_equal_value()
on it.This function does not allow you to customize the messages for
check_column()
. If you want to manually set those, simply usecheck_column()
explicitly.Parameters: - allow_extra – True by default, this determines whether students are allowed to have included other columns in their query result.
- too_many_cols_msg – If specified, this overrides the automatically generated feedback message in
case
allow_extra
is False and the student’s query returned extra columns when comparing the so the solution query result. - expand_msg – if specified, this overrides the automatically generated feedback message that is prepended to feedback messages that are thrown further in the SCT chain.
Example: Consider the following solution and SCT:
# solution SELECT artist_id as id, name FROM artists # sct Ex().check_all_columns() # passing submission SELECT artist_id as id, name FROM artists # failing submission (wrong names) SELECT artist_id, name FROM artists # passing submission (allow_extra is True by default) SELECT artist_id as id, name, label FROM artists
-
check_column
(state, name, missing_msg=None, expand_msg=None)¶ Zoom in on a particular column in the query result, by name.
After zooming in on a column, which is represented as a single-column query result, you can use
has_equal_value()
to verify whether the column in the solution query result matches the column in student query result.Parameters: - name – name of the column to zoom in on.
- missing_msg – if specified, this overrides the automatically generated feedback message in case the column is missing in the student query result.
- expand_msg – if specified, this overrides the automatically generated feedback message that is prepended to feedback messages that are thrown further in the SCT chain.
Example: Suppose we are testing the following SELECT statements
- solution:
SELECT artist_id as id, name FROM artists
- student :
SELECT artist_id, name FROM artists
We can write the following SCTs:
# fails, since no column named id in student result Ex().check_column('id') # passes, since a column named name is in student_result Ex().check_column('name')
-
check_query
(state, query, error_msg=None, expand_msg=None)¶ Run arbitrary queries against to the DB connection to verify the database state.
For queries that do not return any output (INSERTs, UPDATEs, …), you cannot use functions like
check_col()
andhas_equal_value()
to verify the query result.check_query()
will rerun the solution query in the transaction prepared by sqlbackend, and immediately afterwards run the query specified inquery
.Next, it will also run this query after rerunning the student query in a transaction.
Finally, it produces a child state with these results, that you can then chain off of with functions like
check_column()
andhas_equal_value()
.Parameters: - query – A SQL query as a string that is executed after the student query is re-executed.
- error_msg – if specified, this overrides the automatically generated feedback message in case the query generated an error.
- expand_msg – if specified, this overrides the automatically generated feedback message that is prepended to feedback messages that are thrown further in the SCT chain.
Example: Suppose we are checking whether an INSERT happened correctly:
INSERT INTO company VALUES (2, 'filip', 28, 'sql-lane', 42)
We can write the following SCT:
Ex().check_query('SELECT COUNT(*) AS c FROM company').has_equal_value()
-
check_result
(state)¶ High level function which wraps other SCTs for checking results.
check_result()
- uses
lowercase()
, then - runs
check_all_columns()
on the state produced bylowercase()
, then - runs
has_equal_value
on the state produced bycheck_all_columns()
.
- uses
-
check_row
(state, index, missing_msg=None, expand_msg=None)¶ Zoom in on a particular row in the query result, by index.
After zooming in on a row, which is represented as a single-row query result, you can use
has_equal_value()
to verify whether all columns in the zoomed in solution query result have a match in the student query result.Parameters: - index – index of the row to zoom in on (zero-based indexed).
- missing_msg – if specified, this overrides the automatically generated feedback message in case the row is missing in the student query result.
- expand_msg – if specified, this overrides the automatically generated feedback message that is prepended to feedback messages that are thrown further in the SCT chain.
Example: Suppose we are testing the following SELECT statements
- solution:
SELECT artist_id as id, name FROM artists LIMIT 5
- student :
SELECT artist_id, name FROM artists LIMIT 2
We can write the following SCTs:
# fails, since row 3 at index 2 is not in the student result Ex().check_row(2) # passes, since row 2 at index 1 is in the student result Ex().check_row(0)
-
lowercase
(state)¶ Convert all column names to their lower case versions to improve robustness
Example: Suppose we are testing the following SELECT statements
- solution:
SELECT artist_id as id FROM artists
- student :
SELECT artist_id as ID FROM artists
We can write the following SCTs:
# fails, as id and ID have different case Ex().check_column('id').has_equal_value() # passes, as lowercase() is being used Ex().lowercase().check_column('id').has_equal_value()
- solution:
-
has_equal_value
(state, ordered=False, ndigits=None, incorrect_msg=None)¶ Verify if a student and solution query result match up.
This function must always be used after ‘zooming’ in on certain columns or records (check_column, check_row or check_result).
has_equal_value
then goes over all columns that are still left in the solution query result, and compares each column with the corresponding column in the student query result.Parameters: - ordered – if set to False, the default, all rows are sorted (according to the first column and the following columns as tie breakers). if set to True, the order of rows in student and solution query have to match.
- ndigits – if specified, number of decimals to use when comparing column values.
- incorrect_msg – if specified, this overrides the automatically generated feedback message in case a column in the student query result does not match a column in the solution query result.
Example: Suppose we are testing the following SELECT statements
- solution:
SELECT artist_id as id, name FROM artists ORDER BY name
- student :
SELECT artist_id, name FROM artists
We can write the following SCTs:
# passes, as order is not important by default Ex().check_column('name').has_equal_value() # fails, as order is deemed important Ex().check_column('name').has_equal_value(ordered=True) # check_column fails, as id is not in the student query result Ex().check_column('id').has_equal_value() # check_all_columns fails, as id not in the student query result Ex().check_all_columns().has_equal_value()
-
has_ncols
(state, incorrect_msg="Your query returned a table with {{n_stu}} column{{'s' if n_stu > 1 else ''}} while it should return a table with {{n_sol}} column{{'s' if n_sol > 1 else ''}}.")¶ Test whether the student and solution query results have equal numbers of columns.
Parameters: incorrect_msg – If specified, this overrides the automatically generated feedback message in case the number of columns in the student and solution query don’t match.
Example: Consider the following solution and SCT:
# solution SELECT artist_id as id, name FROM artists # sct Ex().has_ncols() # passing submission SELECT artist_id as id, name FROM artists # failing submission (too little columns) SELECT artist_id as id FROM artists # passing submission (two columns, even though not correct ones) SELECT artist_id, label FROM artists
-
has_no_error
(state, incorrect_msg='Your code generated an error. Fix it and try again!')¶ Check whether the submission did not generate a runtime error.
Simply use
Ex().has_no_error()
in your SCT whenever you want to check for errors. By default, after the entire SCT finished executing,sqlwhat
will check for errors before marking the exercise as correct. You can disable this behavior by usingEx().allow_error()
.Parameters: incorrect_msg – If specified, this overrides the automatically generated feedback message in case the student’s query did not return a result.
-
has_nrows
(state, incorrect_msg="Your query returned a table with {{n_stu}} row{{'s' if n_stu > 1 else ''}} while it should return a table with {{n_sol}} row{{'s' if n_sol > 1 else ''}}.")¶ Test whether the student and solution query results have equal numbers of rows.
Parameters: incorrect_msg – If specified, this overrides the automatically generated feedback message in case the number of rows in the student and solution query don’t match.
-
has_result
(state, incorrect_msg='Your query did not return a result.')¶ Checks if the student’s query returned a result.
Parameters: incorrect_msg – If specified, this overrides the automatically generated feedback message in case the student’s query did not return a result.
AST Checks¶
-
check_edge
(state, name, index=0, missing_msg='Check the {ast_path}. Could not find the {index}{field_name}.')¶ Select an attribute from an abstract syntax tree (AST) node, using the attribute name.
Parameters: - state – State instance describing student and solution code. Can be omitted if used with Ex().
- name – the name of the attribute to select from current AST node.
- index – entry to get from a list field. If too few entires, will fail with missing_msg.
- missing_msg – feedback message if attribute is not in student AST.
Example: If both the student and solution code are..
SELECT a FROM b; SELECT x FROM y;
then we can get the from_clause using
# approach 1: with manually created State instance ----- state = State(*args, **kwargs) select = check_node(state, 'SelectStmt', 0) clause = check_edge(select, 'from_clause') # approach 2: with Ex and chaining --------------------- select = Ex().check_node('SelectStmt', 0) # get first select statement clause = select.check_edge('from_clause', None) # get from_clause (a list) clause2 = select.check_edge('from_clause', 0) # get first entry in from_clause
-
check_node
(state, name, index=0, missing_msg='Check the {ast_path}. Could not find the {index}{node_name}.', priority=None)¶ Select a node from abstract syntax tree (AST), using its name and index position.
Parameters: - state – State instance describing student and solution code. Can be omitted if used with Ex().
- name – the name of the abstract syntax tree node to find.
- index – the position of that node (see below for details).
- missing_msg – feedback message if node is not in student AST.
- priority – the priority level of the node being searched for. This determines whether to descend into other AST nodes during the search. Higher priority nodes descend into lower priority. Currently, the only important part of priority is that setting a very high priority (e.g. 99) will search every node.
Example: If both the student and solution code are..
SELECT a FROM b; SELECT x FROM y;
then we can focus on the first select with:
# approach 1: with manually created State instance state = State(*args, **kwargs) new_state = check_node(state, 'SelectStmt', 0) # approach 2: with Ex and chaining new_state = Ex().check_node('SelectStmt', 0)
-
has_code
(state, text, incorrect_msg='Check the {ast_path}. The checker expected to find {text}.', fixed=False)¶ Test whether the student code contains text.
Parameters: - state – State instance describing student and solution code. Can be omitted if used with Ex().
- text – text that student code must contain. Can be a regex pattern or a simple string.
- incorrect_msg – feedback message if text is not in student code.
- fixed – whether to match text exactly, rather than using regular expressions.
Note
Functions like
check_node
focus on certain parts of code. Using these functions followed byhas_code
will only look in the code being focused on.Example: If the student code is..
SELECT a FROM b WHERE id < 100
Then the first test below would (unfortunately) pass, but the second would fail..:
# contained in student code Ex().has_code(text="id < 10") # the $ means that you are matching the end of a line Ex().has_code(text="id < 10$")
By setting
fixed = True
, you can search for fixed strings:# without fixed = True, '*' matches any character Ex().has_code(text="SELECT * FROM b") # passes Ex().has_code(text="SELECT \\* FROM b") # fails Ex().has_code(text="SELECT * FROM b", fixed=True) # fails
You can check only the code corresponding to the WHERE clause, using
where = Ex().check_node('SelectStmt', 0).check_edge('where_clause') where.has_code(text = "id < 10)
-
has_equal_ast
(state, incorrect_msg='Check the {ast_path}. {extra}', sql=None, start='expression', exact=None)¶ Test whether the student and solution code have identical AST representations
Parameters: - state – State instance describing student and solution code. Can be omitted if used with Ex().
- incorrect_msg – feedback message if student and solution ASTs don’t match
- sql – optional code to use instead of the solution ast that is zoomed in on.
- start – if
sql
arg is used, the parser rule to parse the sql code. One of ‘expression’ (the default), ‘subquery’, or ‘sql_script’. - exact – whether to require an exact match (True), or only that the
student AST contains the solution AST. If not specified, this
defaults to
True
ifsql
is not specified, and toFalse
ifsql
is specified. You can always specify it manually.
Example: Example 1 - Suppose the solution code is
SELECT * FROM cities
and you want to verify whether the FROM part is correct:
Ex().check_node('SelectStmt').from_clause().has_equal_ast()
Example 2 - Suppose the solution code is
SELECT * FROM b WHERE id > 1 AND name = 'filip'
Then the following SCT makes sure
id > 1
was used somewhere in the WHERE clause.:Ex().check_node('SelectStmt') \/ .check_edge('where_clause') \/ .has_equal_ast(sql = 'id > 1')
Logic¶
-
classmethod
Ex.
__call__
(state=None)¶ Returns the current code state as a Chain instance.
This allows SCTs to be run without including their 1st argument,
state
.When writing SCTs on DataCamp, no State argument to
Ex
is necessary. The exercise State is built for you.Parameters: state – a State instance, which contains the student/solution code and results.
Example: code
# How to write SCT on DataCamp.com Ex().has_code(text="SELECT id") # Experiment locally - chain off of Ex(), created from state state = SomeStateProducingFunction() Ex(state).has_code(text="SELECT id") # Experiment locally - no Ex(), create and pass state explicitly state = SomeStateProducingFunction() has_code(state, text="SELECT id")
-
check_correct
(state, check, diagnose)¶ Allows feedback from a diagnostic SCT, only if a check SCT fails.
Parameters: - state – State instance describing student and solution code. Can be omitted if used with Ex().
- check – An sct chain that must succeed.
- diagnose – An sct chain to run if the check fails.
Example: The SCT below tests whether students query result is correct, before running diagnostic SCTs..
Ex().check_correct( check_result(), check_node('SelectStmt') )
-
check_not
(state, *tests, msg)¶ Run multiple subtests that should fail. If all subtests fail, returns original state (for chaining)
- This function is currently only tested in working with
has_code()
in the subtests. - This function can be thought as a
NOT(x OR y OR ...)
statement, since all tests it runs must fail - This function can be considered a direct counterpart of multi.
Parameters: - state – State instance describing student and solution code, can be omitted if used with Ex()
- *tests – one or more sub-SCTs to run
- msg – feedback message that is shown in case not all tests specified in
*tests
fail.
Example: Thh SCT below runs two has_code cases..
Ex().check_not( has_code('INNER'), has_code('OUTER'), incorrect_msg="Don't use `INNER` or `OUTER`!" )
If students use
INNER (JOIN)
orOUTER (JOIN)
in their code, this test will fail.- This function is currently only tested in working with
-
check_or
(state, *tests)¶ Test whether at least one SCT passes.
If all of the tests fail, the feedback of the first test will be presented to the student.
Parameters: - state – State instance describing student and solution code, can be omitted if used with Ex()
- tests – one or more sub-SCTs to run
Example: The SCT below tests that the student typed either ‘SELECT’ or ‘WHERE’ (or both)..
Ex().check_or( has_code('SELECT'), has_code('WHERE') )
The SCT below checks that a SELECT statement has at least a WHERE c or LIMIT clause..
Ex().check_node('SelectStmt', 0).check_or( check_edge('where_clause'), check_edge('limit_clause') )
-
disable_highlighting
(state)¶ Disable highlighting in the remainder of the SCT chain.
Include this function if you want to avoid that pythonwhat marks which part of the student submission is incorrect.
-
fail
(state, msg='fail')¶ Always fails the SCT, with an optional msg.
This function takes a single argument,
msg
, that is the feedback given to the student. Note that this would be a terrible idea for grading submissions, but may be useful while writing SCTs. For example, failing a test will highlight the code as if the previous test/check had failed.
-
multi
(state, *tests)¶ Run multiple subtests. Return original state (for chaining).
This function could be thought as an AND statement, since all tests it runs must pass
Parameters: - state – State instance describing student and solution code, can be omitted if used with Ex()
- tests – one or more sub-SCTs to run.
Example: The SCT below checks two has_code cases..
Ex().multi(has_code('SELECT'), has_code('WHERE'))
The SCT below uses
multi
to ‘branch out’ to check that the SELECT statement has both a WHERE and LIMIT clause..Ex().check_node('SelectStmt', 0).multi( check_edge('where_clause'), check_edge('limit_clause') )
Electives¶
-
allow_errors
(state)¶ Allow running the student code to generate errors.
This has to be used only once for every time code is executed or a different xwhat library is used. In most exercises that means it should be used just once.
Example: The following SCT allows the student code to generate errors:
Ex().allow_errors()
-
has_chosen
(state, correct, msgs)¶ Verify exercises of the type MultipleChoiceExercise
Parameters: - state – State instance describing student and solution code. Can be omitted if used with Ex().
- correct – index of correct option, where 1 is the first option.
- msgs – list of feedback messages corresponding to each option.
Example: The following SCT is for a multiple choice exercise with 2 options, the first of which is correct.:
Ex().has_chosen(1, ['Correct!', 'Incorrect. Try again!'])
-
success_msg
(state, msg)¶ Changes the success message to display if submission passes.
Parameters: - state – State instance describing student and solution code. Can be omitted if used with Ex().
- msg – feedback message if student and solution ASTs don’t match
Example: The following SCT changes the success message:
Ex().success_msg("You did it!")