nodes: 0, module; 1, function_definition; 2, function_name:has_equal_value; 3, parameters; 4, block; 5, identifier:state; 6, default_parameter; 7, default_parameter; 8, default_parameter; 9, expression_statement; 10, if_statement; 11, if_statement; 12, comment:# First of all, check if number of rows correspond; 13, expression_statement; 14, if_statement; 15, for_statement; 16, return_statement; 17, identifier:ordered; 18, False; 19, identifier:ndigits; 20, None; 21, identifier:incorrect_msg; 22, None; 23, comment:"""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.
  
      Args:
          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.
          digits: 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()
      """; 24, not_operator; 25, block; 26, comparison_operator:incorrect_msg is None; 27, block; 28, call; 29, not_operator; 30, block; 31, else_clause; 32, pattern_list; 33, call; 34, block; 35, identifier:state; 36, call; 37, raise_statement; 38, identifier:incorrect_msg; 39, None; 40, expression_statement; 41, identifier:has_nrows; 42, argument_list; 43, identifier:ordered; 44, expression_statement; 45, block; 46, identifier:sol_col_name; 47, identifier:sol_col_vals; 48, attribute; 49, argument_list; 50, expression_statement; 51, if_statement; 52, if_statement; 53, identifier:hasattr; 54, argument_list; 55, call; 56, assignment; 57, identifier:state; 58, assignment; 59, expression_statement; 60, expression_statement; 61, identifier:sol_res; 62, identifier:items; 63, assignment; 64, comparison_operator:ndigits is not None; 65, block; 66, comparison_operator:sol_col_vals != stu_col_vals; 67, block; 68, identifier:state; 69, string:"parent"; 70, identifier:ValueError; 71, argument_list; 72, identifier:incorrect_msg; 73, string:"Column `{{col}}` seems to be incorrect.{{' Make sure you arranged the rows correctly.' if ordered else ''}}"; 74, pattern_list; 75, call; 76, assignment; 77, assignment; 78, identifier:stu_col_vals; 79, subscript; 80, identifier:ndigits; 81, None; 82, try_statement; 83, identifier:sol_col_vals; 84, identifier:stu_col_vals; 85, expression_statement; 86, expression_statement; 87, string:"You can only use has_equal_value() on the state resulting from check_column, check_row or check_result."; 88, identifier:stu_res; 89, identifier:sol_res; 90, identifier:sort_rows; 91, argument_list; 92, identifier:stu_res; 93, attribute; 94, identifier:sol_res; 95, attribute; 96, identifier:stu_res; 97, identifier:sol_col_name; 98, block; 99, except_clause; 100, assignment; 101, call; 102, identifier:state; 103, identifier:state; 104, identifier:student_result; 105, identifier:state; 106, identifier:solution_result; 107, expression_statement; 108, expression_statement; 109, block; 110, identifier:_msg; 111, call; 112, attribute; 113, argument_list; 114, assignment; 115, assignment; 116, pass_statement; 117, attribute; 118, argument_list; 119, identifier:state; 120, identifier:do_test; 121, identifier:_msg; 122, identifier:sol_col_vals; 123, call; 124, identifier:stu_col_vals; 125, call; 126, identifier:state; 127, identifier:build_message; 128, identifier:incorrect_msg; 129, keyword_argument; 130, identifier:round_seq; 131, argument_list; 132, identifier:round_seq; 133, argument_list; 134, identifier:fmt_kwargs; 135, dictionary; 136, identifier:sol_col_vals; 137, identifier:ndigits; 138, identifier:stu_col_vals; 139, identifier:ndigits; 140, pair; 141, pair; 142, string:"col"; 143, identifier:sol_col_name; 144, string:"ordered"; 145, identifier:ordered
code: def 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.
  
      Args:
          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.
          digits: 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()
      """
  
      if not hasattr(state, "parent"):
          raise ValueError(
              "You can only use has_equal_value() on the state resulting from check_column, check_row or check_result."
          )
  
      if incorrect_msg is None:
          incorrect_msg = "Column `{{col}}` seems to be incorrect.{{' Make sure you arranged the rows correctly.' if ordered else ''}}"
  
      # First of all, check if number of rows correspond
      has_nrows(state)
  
      if not ordered:
          stu_res, sol_res = sort_rows(state)
      else:
          stu_res = state.student_result
          sol_res = state.solution_result
  
      for sol_col_name, sol_col_vals in sol_res.items():
          stu_col_vals = stu_res[sol_col_name]
          if ndigits is not None:
              try:
                  sol_col_vals = round_seq(sol_col_vals, ndigits)
                  stu_col_vals = round_seq(stu_col_vals, ndigits)
              except:
                  pass
  
          if sol_col_vals != stu_col_vals:
              _msg = state.build_message(
                  incorrect_msg, fmt_kwargs={"col": sol_col_name, "ordered": ordered}
              )
              state.do_test(_msg)
  
      return state