Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
public_tests.py 100.54 KiB
#!/usr/bin/python
# +
import os, json, math, copy
from collections import namedtuple
from bs4 import BeautifulSoup

HIDDEN_FILE = os.path.join("hidden", "hidden_tests.py")
if os.path.exists(HIDDEN_FILE):
    import hidden.hidden_tests as hidn
# -

MAX_FILE_SIZE = 750 # units - KB
REL_TOL = 6e-04  # relative tolerance for floats
ABS_TOL = 15e-03  # absolute tolerance for floats
TOTAL_SCORE = 100 # total score for the project

DF_FILE = 'expected_dfs.html'
PLOT_FILE = 'expected_plots.json'

PASS = "All test cases passed!"

TEXT_FORMAT = "TEXT_FORMAT"  # question type when expected answer is a type, str, int, float, or bool
TEXT_FORMAT_UNORDERED_LIST = "TEXT_FORMAT_UNORDERED_LIST"  # question type when the expected answer is a list or a set where the order does *not* matter
TEXT_FORMAT_ORDERED_LIST = "TEXT_FORMAT_ORDERED_LIST"  # question type when the expected answer is a list or tuple where the order does matter
TEXT_FORMAT_DICT = "TEXT_FORMAT_DICT"  # question type when the expected answer is a dictionary
TEXT_FORMAT_SPECIAL_ORDERED_LIST = "TEXT_FORMAT_SPECIAL_ORDERED_LIST"  # question type when the expected answer is a list where order does matter, but with possible ties. Elements are ordered according to values in special_ordered_json (with ties allowed)
TEXT_FORMAT_NAMEDTUPLE = "TEXT_FORMAT_NAMEDTUPLE"  # question type when expected answer is a namedtuple
PNG_FORMAT_SCATTER = "PNG_FORMAT_SCATTER" # question type when the expected answer is a scatter plot
HTML_FORMAT = "HTML_FORMAT" # question type when the expected answer is a DataFrame
FILE_JSON_FORMAT = "FILE_JSON_FORMAT" # question type when the expected answer is a JSON file
SLASHES = " SLASHES" # question SUFFIX when expected answer contains paths with slashes

def get_expected_format():
    """get_expected_format() returns a dict mapping each question to the format
    of the expected answer."""
    expected_format = {'q1': 'TEXT_FORMAT',
                       'q2': 'TEXT_FORMAT',
                       'q3': 'TEXT_FORMAT',
                       'q4': 'TEXT_FORMAT',
                       'q5': 'TEXT_FORMAT',
                       'q6': 'TEXT_FORMAT',
                       'q7': 'TEXT_FORMAT',
                       'q8': 'TEXT_FORMAT',
                       'q9': 'TEXT_FORMAT',
                       'q10': 'TEXT_FORMAT',
                       'q11': 'TEXT_FORMAT',
                       'q12': 'TEXT_FORMAT',
                       'q13': 'TEXT_FORMAT',
                       'q14': 'TEXT_FORMAT',
                       'q15': 'TEXT_FORMAT',
                       'q16': 'TEXT_FORMAT',
                       'q17': 'TEXT_FORMAT_DICT',
                       'q18': 'TEXT_FORMAT_DICT',
                       'q19': 'TEXT_FORMAT',
                       'q20': 'TEXT_FORMAT',
                       'q21': 'TEXT_FORMAT_DICT',
                       'q22': 'TEXT_FORMAT',
                       'q23': 'TEXT_FORMAT',
                       'q24': 'TEXT_FORMAT_DICT',
                       'q25': 'TEXT_FORMAT_DICT',
                       'q26': 'TEXT_FORMAT',
                       'q27': 'TEXT_FORMAT',
                       'q28': 'TEXT_FORMAT_DICT',
                       'q29': 'TEXT_FORMAT_DICT',
                       'q30': 'TEXT_FORMAT_DICT',
                       'q31': 'TEXT_FORMAT_DICT',
                       'q32': 'TEXT_FORMAT_DICT',
                       'q33': 'TEXT_FORMAT_DICT',
                       'q34': 'TEXT_FORMAT_DICT',
                       'q35': 'TEXT_FORMAT_DICT',
                       'q36': 'TEXT_FORMAT_DICT',
                       'q37': 'TEXT_FORMAT_DICT',
                       'q38': 'TEXT_FORMAT',
                       'q39': 'TEXT_FORMAT',
                       'q40': 'TEXT_FORMAT'}
    return expected_format


def get_expected_json():
    """get_expected_json() returns a dict mapping each question to the expected
    answer (if the format permits it)."""
    expected_json = {'q1': 'E. Haaland',
                     'q2': 'Paris Saint Germain',
                     'q3': 32,
                     'q4': '188cm',
                     'q5': 188,
                     'q6': '€250K',
                     'q7': '€158.5M',
                     'q8': 250000,
                     'q9': 158500000,
                     'q10': 0,
                     'q11': 500,
                     'q12': 44,
                     'q13': 63.0,
                     'q14': 185,
                     'q15': 'Right',
                     'q16': 'Arsenal',
                     'q17': {'ID': 239085,
                             'Name': 'E. Haaland',
                             'Age': 22,
                             'Nationality': 'Norway',
                             'Team': 'Manchester City',
                             'League': 'Premier League (England)',
                             'Value': 185000000,
                             'Wage': 340000,
                             'Attacking': 78.6,
                             'Movement': 83.6,
                             'Defending': 38.0,
                             'Goalkeeping': 10.4,
                             'Overall rating': 91,
                             'Position': 'ST',
                             'Height': 195,
                             'Preferred foot': 'Left'},
                     'q18': {'ID': 231747,
                             'Name': 'K. Mbappé',
                             'Age': 24,
                             'Nationality': 'France',
                             'Team': 'Paris Saint Germain',
                             'League': 'Ligue 1 (France)',
                             'Value': 181500000,
                             'Wage': 230000,
                             'Attacking': 83.0,
                             'Movement': 92.4,
                             'Defending': 30.7,
                             'Goalkeeping': 8.4,
                             'Overall rating': 91,
                             'Position': 'ST',
                             'Height': 182,
                             'Preferred foot': 'Right'},
                     'q19': 'ST',
                     'q20': 182,
                     'q21': {239085: 83.6,
                             231747: 92.4,
                             192985: 77.6,
                             202126: 74.0,
                             192119: 58.0,
                             188545: 80.8,
                             165153: 79.6,
                             158023: 87.0,
                             239818: 65.6,
                             238794: 90.8,
                             231866: 66.8,
                             209331: 90.2,
                             203376: 70.0,
                             200145: 67.4,
                             190871: 87.4,
                             212622: 79.6,
                             212198: 78.0,
                             230621: 58.2,
                             228702: 83.4,
                             222665: 79.2,
                             200104: 83.4,
                             193080: 56.4,
                             177003: 82.8,
                             256790: 88.2,
                             253163: 70.2,
                             252371: 79.8,
                             251854: 84.4,
                             246669: 85.2,
                             235243: 66.4,
                             232411: 85.2,
                             231443: 87.6,
                             215914: 79.4,
                             211110: 83.4,
                             208722: 86.4,
                             204485: 84.4,
                             199556: 76.6,
                             186942: 73.0,
                             182521: 65.0,
                             20801: 75.4,
                             237692: 85.2,
                             268965: 55.8,
                             268963: 54.0,
                             268280: 38.4,
                             262760: 49.8,
                             258802: 58.2,
                             269032: 60.4,
                             269038: 61.0,
                             268279: 36.6,
                             213799: 45.0,
                             183162: 34.0},
                     'q22': 65.6,
                     'q23': 90.2,
                     'q24': {239085: {'ID': 239085,
                                      'Name': 'E. Haaland',
                                      'Age': 22,
                                      'Nationality': 'Norway',
                                      'Team': 'Manchester City',
                                      'League': 'Premier League (England)',
                                      'Value': 185000000,
                                      'Wage': 340000,
                                      'Attacking': 78.6,
                                      'Movement': 83.6,
                                      'Defending': 38.0,
                                      'Goalkeeping': 10.4,
                                      'Overall rating': 91,
                                      'Position': 'ST',
                                      'Height': 195,
                                      'Preferred foot': 'Left'},
                             231747: {'ID': 231747,
                                      'Name': 'K. Mbappé',
                                      'Age': 24,
                                      'Nationality': 'France',
                                      'Team': 'Paris Saint Germain',
                                      'League': 'Ligue 1 (France)',
                                      'Value': 181500000,
                                      'Wage': 230000,
                                      'Attacking': 83.0,
                                      'Movement': 92.4,
                                      'Defending': 30.7,
                                      'Goalkeeping': 8.4,
                                      'Overall rating': 91,
                                      'Position': 'ST',
                                      'Height': 182,
                                      'Preferred foot': 'Right'},
                             192985: {'ID': 192985,
                                      'Name': 'K. De Bruyne',
                                      'Age': 32,
                                      'Nationality': 'Belgium',
                                      'Team': 'Manchester City',
                                      'League': 'Premier League (England)',
                                      'Value': 103000000,
                                      'Wage': 350000,
                                      'Attacking': 82.4,
                                      'Movement': 77.6,
                                      'Defending': 63.0,
                                      'Goalkeeping': 11.2,
                                      'Overall rating': 91,
                                      'Position': 'CM',
                                      'Height': 181,
                                      'Preferred foot': 'Right'},
                             202126: {'ID': 202126,
                                      'Name': 'H. Kane',
                                      'Age': 29,
                                      'Nationality': 'England',
                                      'Team': 'FC Bayern München',
                                      'League': 'Bundesliga (Germany)',
                                      'Value': 119500000,
                                      'Wage': 170000,
                                      'Attacking': 88.0,
                                      'Movement': 74.0,
                                      'Defending': 43.3,
                                      'Goalkeeping': 10.8,
                                      'Overall rating': 90,
                                      'Position': 'ST',
                                      'Height': 188,
                                      'Preferred foot': 'Right'},
                             192119: {'ID': 192119,
                                      'Name': 'T. Courtois',
                                      'Age': 31,
                                      'Nationality': 'Belgium',
                                      'Team': 'Real Madrid',
                                      'League': 'La Liga (Spain)',
                                      'Value': 63000000,
                                      'Wage': 250000,
                                      'Attacking': 17.2,
                                      'Movement': 58.0,
                                      'Defending': 18.0,
                                      'Goalkeeping': 86.6,
                                      'Overall rating': 90,
                                      'Position': 'GK',
                                      'Height': 199,
                                      'Preferred foot': 'Left'},
                             188545: {'ID': 188545,
                                      'Name': 'R. Lewandowski',
                                      'Age': 34,
                                      'Nationality': 'Poland',
                                      'Team': 'FC Barcelona',
                                      'League': 'La Liga (Spain)',
                                      'Value': 58000000,
                                      'Wage': 340000,
                                      'Attacking': 86.6,
                                      'Movement': 80.8,
                                      'Defending': 32.0,
                                      'Goalkeeping': 10.2,
                                      'Overall rating': 90,
                                      'Position': 'ST',
                                      'Height': 185,
                                      'Preferred foot': 'Right'},
                             165153: {'ID': 165153,
                                      'Name': 'K. Benzema',
                                      'Age': 35,
                                      'Nationality': 'France',
                                      'Team': 'Al Ittihad',
                                      'League': 'Pro League (Saudi Arabia)',
                                      'Value': 51000000,
                                      'Wage': 95000,
                                      'Attacking': 86.6,
                                      'Movement': 79.6,
                                      'Defending': 28.3,
                                      'Goalkeeping': 8.2,
                                      'Overall rating': 90,
                                      'Position': 'CF',
                                      'Height': 185,
                                      'Preferred foot': 'Right'},
                             158023: {'ID': 158023,
                                      'Name': 'L. Messi',
                                      'Age': 36,
                                      'Nationality': 'Argentina',
                                      'Team': 'Inter Miami',
                                      'League': 'Major League Soccer (United States)',
                                      'Value': 41000000,
                                      'Wage': 23000,
                                      'Attacking': 81.8,
                                      'Movement': 87.0,
                                      'Defending': 26.3,
                                      'Goalkeeping': 10.8,
                                      'Overall rating': 90,
                                      'Position': 'CAM',
                                      'Height': 169,
                                      'Preferred foot': 'Left'},
                             239818: {'ID': 239818,
                                      'Name': 'Rúben Dias',
                                      'Age': 26,
                                      'Nationality': 'Portugal',
                                      'Team': 'Manchester City',
                                      'League': 'Premier League (England)',
                                      'Value': 106500000,
                                      'Wage': 250000,
                                      'Attacking': 57.0,
                                      'Movement': 65.6,
                                      'Defending': 89.7,
                                      'Goalkeeping': 9.4,
                                      'Overall rating': 89,
                                      'Position': 'CB',
                                      'Height': 187,
                                      'Preferred foot': 'Right'},
                             238794: {'ID': 238794,
                                      'Name': 'Vini Jr.',
                                      'Age': 22,
                                      'Nationality': 'Brazil',
                                      'Team': 'Real Madrid',
                                      'League': 'La Liga (Spain)',
                                      'Value': 158500000,
                                      'Wage': 310000,
                                      'Attacking': 73.8,
                                      'Movement': 90.8,
                                      'Defending': 25.0,
                                      'Goalkeeping': 7.2,
                                      'Overall rating': 89,
                                      'Position': 'LW',
                                      'Height': 176,
                                      'Preferred foot': 'Right'},
                             231866: {'ID': 231866,
                                      'Name': 'Rodri',
                                      'Age': 27,
                                      'Nationality': 'Spain',
                                      'Team': 'Manchester City',
                                      'League': 'Premier League (England)',
                                      'Value': 105500000,
                                      'Wage': 250000,
                                      'Attacking': 71.2,
                                      'Movement': 66.8,
                                      'Defending': 84.3,
                                      'Goalkeeping': 9.8,
                                      'Overall rating': 89,
                                      'Position': 'CDM',
                                      'Height': 191,
                                      'Preferred foot': 'Right'},
                             209331: {'ID': 209331,
                                      'Name': 'M. Salah',
                                      'Age': 31,
                                      'Nationality': 'Egypt',
                                      'Team': 'Liverpool',
                                      'League': 'Premier League (England)',
                                      'Value': 85500000,
                                      'Wage': 260000,
                                      'Attacking': 79.8,
                                      'Movement': 90.2,
                                      'Defending': 40.7,
                                      'Goalkeeping': 12.4,
                                      'Overall rating': 89,
                                      'Position': 'RW',
                                      'Height': 175,
                                      'Preferred foot': 'Left'},
                             203376: {'ID': 203376,
                                      'Name': 'V. van Dijk',
                                      'Age': 31,
                                      'Nationality': 'Netherlands',
                                      'Team': 'Liverpool',
                                      'League': 'Premier League (England)',
                                      'Value': 70500000,
                                      'Wage': 220000,
                                      'Attacking': 63.0,
                                      'Movement': 70.0,
                                      'Defending': 89.0,
                                      'Goalkeeping': 11.6,
                                      'Overall rating': 89,
                                      'Position': 'CB',
                                      'Height': 193,
                                      'Preferred foot': 'Right'},
                             200145: {'ID': 200145,
                                      'Name': 'Casemiro',
                                      'Age': 31,
                                      'Nationality': 'Brazil',
                                      'Team': 'Manchester United',
                                      'League': 'Premier League (England)',
                                      'Value': 72000000,
                                      'Wage': 240000,
                                      'Attacking': 74.6,
                                      'Movement': 67.4,
                                      'Defending': 89.0,
                                      'Goalkeeping': 13.4,
                                      'Overall rating': 89,
                                      'Position': 'CDM',
                                      'Height': 185,
                                      'Preferred foot': 'Right'},
                             190871: {'ID': 190871,
                                      'Name': 'Neymar Jr',
                                      'Age': 31,
                                      'Nationality': 'Brazil',
                                      'Team': 'Al Hilal',
                                      'League': 'Pro League (Saudi Arabia)',
                                      'Value': 85500000,
                                      'Wage': 115000,
                                      'Attacking': 80.0,
                                      'Movement': 87.4,
                                      'Defending': 32.0,
                                      'Goalkeeping': 11.8,
                                      'Overall rating': 89,
                                      'Position': 'LW',
                                      'Height': 175,
                                      'Preferred foot': 'Right'},
                             212622: {'ID': 212622,
                                      'Name': 'J. Kimmich',
                                      'Age': 28,
                                      'Nationality': 'Germany',
                                      'Team': 'FC Bayern München',
                                      'League': 'Bundesliga (Germany)',
                                      'Value': 88000000,
                                      'Wage': 130000,
                                      'Attacking': 77.6,
                                      'Movement': 79.6,
                                      'Defending': 81.3,
                                      'Goalkeeping': 12.0,
                                      'Overall rating': 88,
                                      'Position': 'CDM',
                                      'Height': 177,
                                      'Preferred foot': 'Right'},
                             212198: {'ID': 212198,
                                      'Name': 'Bruno Fernandes',
                                      'Age': 28,
                                      'Nationality': 'Portugal',
                                      'Team': 'Manchester United',
                                      'League': 'Premier League (England)',
                                      'Value': 92000000,
                                      'Wage': 260000,
                                      'Attacking': 82.6,
                                      'Movement': 78.0,
                                      'Defending': 69.3,
                                      'Goalkeeping': 12.6,
                                      'Overall rating': 88,
                                      'Position': 'CAM',
                                      'Height': 179,
                                      'Preferred foot': 'Right'},
                             230621: {'ID': 230621,
                                      'Name': 'G. Donnarumma',
                                      'Age': 24,
                                      'Nationality': 'Italy',
                                      'Team': 'Paris Saint Germain',
                                      'League': 'Ligue 1 (France)',
                                      'Value': 85000000,
                                      'Wage': 90000,
                                      'Attacking': 16.0,
                                      'Movement': 58.2,
                                      'Defending': 16.7,
                                      'Goalkeeping': 84.6,
                                      'Overall rating': 87,
                                      'Position': 'GK',
                                      'Height': 196,
                                      'Preferred foot': 'Right'},
                             228702: {'ID': 228702,
                                      'Name': 'F. de Jong',
                                      'Age': 26,
                                      'Nationality': 'Netherlands',
                                      'Team': 'FC Barcelona',
                                      'League': 'La Liga (Spain)',
                                      'Value': 103500000,
                                      'Wage': 240000,
                                      'Attacking': 76.6,
                                      'Movement': 83.4,
                                      'Defending': 76.3,
                                      'Goalkeeping': 9.8,
                                      'Overall rating': 87,
                                      'Position': 'CM',
                                      'Height': 181,
                                      'Preferred foot': 'Right'},
                             222665: {'ID': 222665,
                                      'Name': 'M. Ødegaard',
                                      'Age': 24,
                                      'Nationality': 'Norway',
                                      'Team': 'Arsenal',
                                      'League': 'Premier League (England)',
                                      'Value': 109000000,
                                      'Wage': 170000,
                                      'Attacking': 78.2,
                                      'Movement': 79.2,
                                      'Defending': 58.7,
                                      'Goalkeeping': 12.4,
                                      'Overall rating': 87,
                                      'Position': 'CAM',
                                      'Height': 178,
                                      'Preferred foot': 'Left'},
                             200104: {'ID': 200104,
                                      'Name': 'H. Son',
                                      'Age': 30,
                                      'Nationality': 'Korea Republic',
                                      'Team': 'Tottenham Hotspur',
                                      'League': 'Premier League (England)',
                                      'Value': 77000000,
                                      'Wage': 170000,
                                      'Attacking': 80.2,
                                      'Movement': 83.4,
                                      'Defending': 38.0,
                                      'Goalkeeping': 10.6,
                                      'Overall rating': 87,
                                      'Position': 'LW',
                                      'Height': 183,
                                      'Preferred foot': 'Right'},
                             193080: {'ID': 193080,
                                      'Name': 'De Gea',
                                      'Age': 31,
                                      'Nationality': 'Spain',
                                      'Team': 'Manchester United',
                                      'League': 'Premier League (England)',
                                      'Value': 42000000,
                                      'Wage': 150000,
                                      'Attacking': 20.6,
                                      'Movement': 56.4,
                                      'Defending': 19.0,
                                      'Goalkeeping': 82.2,
                                      'Overall rating': 87,
                                      'Position': 'GK',
                                      'Height': 192,
                                      'Preferred foot': 'Right'},
                             177003: {'ID': 177003,
                                      'Name': 'L. Modrić',
                                      'Age': 37,
                                      'Nationality': 'Croatia',
                                      'Team': 'Real Madrid',
                                      'League': 'La Liga (Spain)',
                                      'Value': 25000000,
                                      'Wage': 190000,
                                      'Attacking': 76.0,
                                      'Movement': 82.8,
                                      'Defending': 71.7,
                                      'Goalkeeping': 10.4,
                                      'Overall rating': 87,
                                      'Position': 'CM',
                                      'Height': 172,
                                      'Preferred foot': 'Right'},
                             256790: {'ID': 256790,
                                      'Name': 'J. Musiala',
                                      'Age': 20,
                                      'Nationality': 'Germany',
                                      'Team': 'FC Bayern München',
                                      'League': 'Bundesliga (Germany)',
                                      'Value': 134500000,
                                      'Wage': 79000,
                                      'Attacking': 69.4,
                                      'Movement': 88.2,
                                      'Defending': 65.0,
                                      'Goalkeeping': 8.4,
                                      'Overall rating': 86,
                                      'Position': 'CAM',
                                      'Height': 184,
                                      'Preferred foot': 'Right'},
                             253163: {'ID': 253163,
                                      'Name': 'R. Araujo',
                                      'Age': 24,
                                      'Nationality': 'Uruguay',
                                      'Team': 'FC Barcelona',
                                      'League': 'La Liga (Spain)',
                                      'Value': 93000000,
                                      'Wage': 175000,
                                      'Attacking': 62.6,
                                      'Movement': 70.2,
                                      'Defending': 86.0,
                                      'Goalkeeping': 10.6,
                                      'Overall rating': 86,
                                      'Position': 'CB',
                                      'Height': 188,
                                      'Preferred foot': 'Right'},
                             252371: {'ID': 252371,
                                      'Name': 'J. Bellingham',
                                      'Age': 20,
                                      'Nationality': 'England',
                                      'Team': 'Real Madrid',
                                      'League': 'La Liga (Spain)',
                                      'Value': 100500000,
                                      'Wage': 175000,
                                      'Attacking': 74.8,
                                      'Movement': 79.8,
                                      'Defending': 77.7,
                                      'Goalkeeping': 9.6,
                                      'Overall rating': 86,
                                      'Position': 'CM',
                                      'Height': 186,
                                      'Preferred foot': 'Right'},
                             251854: {'ID': 251854,
                                      'Name': 'Pedri',
                                      'Age': 20,
                                      'Nationality': 'Spain',
                                      'Team': 'FC Barcelona',
                                      'League': 'La Liga (Spain)',
                                      'Value': 105000000,
                                      'Wage': 165000,
                                      'Attacking': 66.8,
                                      'Movement': 84.4,
                                      'Defending': 70.0,
                                      'Goalkeeping': 9.2,
                                      'Overall rating': 86,
                                      'Position': 'CM',
                                      'Height': 174,
                                      'Preferred foot': 'Right'},
                             246669: {'ID': 246669,
                                      'Name': 'B. Saka',
                                      'Age': 21,
                                      'Nationality': 'England',
                                      'Team': 'Arsenal',
                                      'League': 'Premier League (England)',
                                      'Value': 99000000,
                                      'Wage': 150000,
                                      'Attacking': 74.2,
                                      'Movement': 85.2,
                                      'Defending': 60.7,
                                      'Goalkeeping': 10.0,
                                      'Overall rating': 86,
                                      'Position': 'RW',
                                      'Height': 178,
                                      'Preferred foot': 'Left'},
                             235243: {'ID': 235243,
                                      'Name': 'M. de Ligt',
                                      'Age': 23,
                                      'Nationality': 'Netherlands',
                                      'Team': 'FC Bayern München',
                                      'League': 'Bundesliga (Germany)',
                                      'Value': 83000000,
                                      'Wage': 84000,
                                      'Attacking': 62.2,
                                      'Movement': 66.4,
                                      'Defending': 86.7,
                                      'Goalkeeping': 11.2,
                                      'Overall rating': 86,
                                      'Position': 'CB',
                                      'Height': 189,
                                      'Preferred foot': 'Right'},
                             232411: {'ID': 232411,
                                      'Name': 'C. Nkunku',
                                      'Age': 25,
                                      'Nationality': 'France',
                                      'Team': 'Chelsea',
                                      'League': 'Premier League (England)',
                                      'Value': 86500000,
                                      'Wage': 185000,
                                      'Attacking': 76.6,
                                      'Movement': 85.2,
                                      'Defending': 60.0,
                                      'Goalkeeping': 8.6,
                                      'Overall rating': 86,
                                      'Position': 'CAM',
                                      'Height': 175,
                                      'Preferred foot': 'Right'},
                             231443: {'ID': 231443,
                                      'Name': 'O. Dembélé',
                                      'Age': 26,
                                      'Nationality': 'France',
                                      'Team': 'Paris Saint Germain',
                                      'League': 'Ligue 1 (France)',
                                      'Value': 80000000,
                                      'Wage': 150000,
                                      'Attacking': 72.0,
                                      'Movement': 87.6,
                                      'Defending': 35.0,
                                      'Goalkeeping': 9.8,
                                      'Overall rating': 86,
                                      'Position': 'RW',
                                      'Height': 178,
                                      'Preferred foot': 'Left'},
                             215914: {'ID': 215914,
                                      'Name': 'N. Kanté',
                                      'Age': 32,
                                      'Nationality': 'France',
                                      'Team': 'Al Ittihad',
                                      'League': 'Pro League (Saudi Arabia)',
                                      'Value': 45000000,
                                      'Wage': 73000,
                                      'Attacking': 64.4,
                                      'Movement': 79.4,
                                      'Defending': 87.7,
                                      'Goalkeeping': 10.8,
                                      'Overall rating': 86,
                                      'Position': 'CDM',
                                      'Height': 168,
                                      'Preferred foot': 'Right'},
                             211110: {'ID': 211110,
                                      'Name': 'P. Dybala',
                                      'Age': 29,
                                      'Nationality': 'Argentina',
                                      'Team': 'Roma',
                                      'League': 'Serie A (Italy)',
                                      'Value': 68000000,
                                      'Wage': 130000,
                                      'Attacking': 79.8,
                                      'Movement': 83.4,
                                      'Defending': 37.3,
                                      'Goalkeeping': 5.2,
                                      'Overall rating': 86,
                                      'Position': 'CAM',
                                      'Height': 177,
                                      'Preferred foot': 'Left'},
                             208722: {'ID': 208722,
                                      'Name': 'S. Mané',
                                      'Age': 31,
                                      'Nationality': 'Senegal',
                                      'Team': 'Al Nassr',
                                      'League': 'Pro League (Saudi Arabia)',
                                      'Value': 56500000,
                                      'Wage': 80000,
                                      'Attacking': 80.0,
                                      'Movement': 86.4,
                                      'Defending': 40.7,
                                      'Goalkeeping': 11.2,
                                      'Overall rating': 86,
                                      'Position': 'LM',
                                      'Height': 174,
                                      'Preferred foot': 'Right'},
                             204485: {'ID': 204485,
                                      'Name': 'R. Mahrez',
                                      'Age': 32,
                                      'Nationality': 'Algeria',
                                      'Team': 'Al Ahli Jeddah',
                                      'League': 'Pro League (Saudi Arabia)',
                                      'Value': 54000000,
                                      'Wage': 72000,
                                      'Attacking': 75.2,
                                      'Movement': 84.4,
                                      'Defending': 32.7,
                                      'Goalkeeping': 10.8,
                                      'Overall rating': 86,
                                      'Position': 'RM',
                                      'Height': 179,
                                      'Preferred foot': 'Left'},
                             199556: {'ID': 199556,
                                      'Name': 'M. Verratti',
                                      'Age': 30,
                                      'Nationality': 'Italy',
                                      'Team': 'Paris Saint Germain',
                                      'League': 'Ligue 1 (France)',
                                      'Value': 65000000,
                                      'Wage': 135000,
                                      'Attacking': 70.6,
                                      'Movement': 76.6,
                                      'Defending': 81.3,
                                      'Goalkeeping': 12.8,
                                      'Overall rating': 86,
                                      'Position': 'CM',
                                      'Height': 165,
                                      'Preferred foot': 'Right'},
                             186942: {'ID': 186942,
                                      'Name': 'İ. Gündoğan',
                                      'Age': 32,
                                      'Nationality': 'Germany',
                                      'Team': 'FC Barcelona',
                                      'League': 'La Liga (Spain)',
                                      'Value': 53500000,
                                      'Wage': 220000,
                                      'Attacking': 74.4,
                                      'Movement': 73.0,
                                      'Defending': 73.0,
                                      'Goalkeeping': 9.6,
                                      'Overall rating': 86,
                                      'Position': 'CM',
                                      'Height': 180,
                                      'Preferred foot': 'Right'},
                             182521: {'ID': 182521,
                                      'Name': 'T. Kroos',
                                      'Age': 33,
                                      'Nationality': 'Germany',
                                      'Team': 'Real Madrid',
                                      'League': 'La Liga (Spain)',
                                      'Value': 42000000,
                                      'Wage': 240000,
                                      'Attacking': 79.2,
                                      'Movement': 65.0,
                                      'Defending': 67.0,
                                      'Goalkeeping': 10.2,
                                      'Overall rating': 86,
                                      'Position': 'CM',
                                      'Height': 183,
                                      'Preferred foot': 'Right'},
                             20801: {'ID': 20801,
                                     'Name': 'Cristiano Ronaldo',
                                     'Age': 38,
                                     'Nationality': 'Portugal',
                                     'Team': 'Al Nassr',
                                     'League': 'Pro League (Saudi Arabia)',
                                     'Value': 23000000,
                                     'Wage': 66000,
                                     'Attacking': 81.8,
                                     'Movement': 75.4,
                                     'Defending': 26.7,
                                     'Goalkeeping': 11.6,
                                     'Overall rating': 86,
                                     'Position': 'ST',
                                     'Height': 187,
                                     'Preferred foot': 'Right'},
                             237692: {'ID': 237692,
                                      'Name': 'P. Foden',
                                      'Age': 23,
                                      'Nationality': 'England',
                                      'Team': 'Manchester City',
                                      'League': 'Premier League (England)',
                                      'Value': 81500000,
                                      'Wage': 180000,
                                      'Attacking': 70.2,
                                      'Movement': 85.2,
                                      'Defending': 55.3,
                                      'Goalkeeping': 10.4,
                                      'Overall rating': 85,
                                      'Position': 'CAM',
                                      'Height': 171,
                                      'Preferred foot': 'Left'},
                             268965: {'ID': 268965,
                                      'Name': 'Zhang Yujun',
                                      'Age': 19,
                                      'Nationality': 'China PR',
                                      'Team': 'Hebei',
                                      'League': 'Super League (China PR)',
                                      'Value': 100000,
                                      'Wage': 1000,
                                      'Attacking': 41.4,
                                      'Movement': 55.8,
                                      'Defending': 43.0,
                                      'Goalkeeping': 10.8,
                                      'Overall rating': 46,
                                      'Position': 'CM',
                                      'Height': 176,
                                      'Preferred foot': 'Right'},
                             268963: {'ID': 268963,
                                      'Name': 'Zhang Jiahui',
                                      'Age': 19,
                                      'Nationality': 'China PR',
                                      'Team': 'Hebei',
                                      'League': 'Super League (China PR)',
                                      'Value': 100000,
                                      'Wage': 900,
                                      'Attacking': 41.2,
                                      'Movement': 54.0,
                                      'Defending': 43.7,
                                      'Goalkeeping': 10.0,
                                      'Overall rating': 46,
                                      'Position': 'CM',
                                      'Height': 182,
                                      'Preferred foot': 'Right'},
                             268280: {'ID': 268280,
                                      'Name': 'L. Hebbelmann',
                                      'Age': 22,
                                      'Nationality': 'Germany',
                                      'Team': 'Meppen',
                                      'League': '3. Liga (Germany)',
                                      'Value': 60000,
                                      'Wage': 500,
                                      'Attacking': 35.6,
                                      'Movement': 38.4,
                                      'Defending': 44.3,
                                      'Goalkeeping': 10.4,
                                      'Overall rating': 46,
                                      'Position': 'CDM',
                                      'Height': 181,
                                      'Preferred foot': 'Right'},
                             262760: {'ID': 262760,
                                      'Name': 'N. Logue',
                                      'Age': 22,
                                      'Nationality': 'Republic of Ireland',
                                      'Team': 'Finn Harps',
                                      'League': 'Premier Division (Republic of Ireland)',
                                      'Value': 100000,
                                      'Wage': 500,
                                      'Attacking': 40.0,
                                      'Movement': 49.8,
                                      'Defending': 43.3,
                                      'Goalkeeping': 7.4,
                                      'Overall rating': 46,
                                      'Position': 'CAM',
                                      'Height': 178,
                                      'Preferred foot': 'Right'},
                             258802: {'ID': 258802,
                                      'Name': 'B. Singh',
                                      'Age': 22,
                                      'Nationality': 'India',
                                      'Team': 'Jamshedpur',
                                      'League': 'Indian Super League (India)',
                                      'Value': 110000,
                                      'Wage': 500,
                                      'Attacking': 41.4,
                                      'Movement': 58.2,
                                      'Defending': 22.7,
                                      'Goalkeeping': 10.8,
                                      'Overall rating': 46,
                                      'Position': 'ST',
                                      'Height': 172,
                                      'Preferred foot': 'Right'},
                             269032: {'ID': 269032,
                                      'Name': 'Chen Zeshi',
                                      'Age': 16,
                                      'Nationality': 'China PR',
                                      'Team': 'Shandong Taishan',
                                      'League': 'Super League (China PR)',
                                      'Value': 100000,
                                      'Wage': 500,
                                      'Attacking': 41.0,
                                      'Movement': 60.4,
                                      'Defending': 39.7,
                                      'Goalkeeping': 9.6,
                                      'Overall rating': 45,
                                      'Position': 'RM',
                                      'Height': 180,
                                      'Preferred foot': 'Right'},
                             269038: {'ID': 269038,
                                      'Name': 'Zhang Wenxuan',
                                      'Age': 16,
                                      'Nationality': 'China PR',
                                      'Team': 'Guangzhou',
                                      'League': 'Super League (China PR)',
                                      'Value': 110000,
                                      'Wage': 500,
                                      'Attacking': 37.2,
                                      'Movement': 61.0,
                                      'Defending': 42.7,
                                      'Goalkeeping': 11.6,
                                      'Overall rating': 44,
                                      'Position': 'CB',
                                      'Height': 175,
                                      'Preferred foot': 'Right'},
                             268279: {'ID': 268279,
                                      'Name': 'J. Looschen',
                                      'Age': 24,
                                      'Nationality': 'Germany',
                                      'Team': 'Meppen',
                                      'League': '3. Liga (Germany)',
                                      'Value': 60000,
                                      'Wage': 500,
                                      'Attacking': 38.0,
                                      'Movement': 36.6,
                                      'Defending': 23.7,
                                      'Goalkeeping': 9.8,
                                      'Overall rating': 44,
                                      'Position': 'CAM',
                                      'Height': 178,
                                      'Preferred foot': 'Right'},
                             213799: {'ID': 213799,
                                      'Name': 'H. McFadden',
                                      'Age': 19,
                                      'Nationality': 'Republic of Ireland',
                                      'Team': 'Sligo Rovers',
                                      'League': 'Premier Division (Republic of Ireland)',
                                      'Value': 20000,
                                      'Wage': 500,
                                      'Attacking': 29.4,
                                      'Movement': 45.0,
                                      'Defending': 46.3,
                                      'Goalkeeping': 11.6,
                                      'Overall rating': 44,
                                      'Position': 'CB',
                                      'Height': 182,
                                      'Preferred foot': 'Right'},
                             183162: {'ID': 183162,
                                      'Name': 'J. Yates',
                                      'Age': 19,
                                      'Nationality': 'England',
                                      'Team': 'Rotherham United',
                                      'League': 'Championship (England)',
                                      'Value': 0,
                                      'Wage': 0,
                                      'Attacking': 26.2,
                                      'Movement': 34.0,
                                      'Defending': 14.0,
                                      'Goalkeeping': 13.8,
                                      'Overall rating': 40,
                                      'Position': 'ST',
                                      'Height': 170,
                                      'Preferred foot': 'Right'}},
                     'q25': {'ID': 204485,
                             'Name': 'R. Mahrez',
                             'Age': 32,
                             'Nationality': 'Algeria',
                             'Team': 'Al Ahli Jeddah',
                             'League': 'Pro League (Saudi Arabia)',
                             'Value': 54000000,
                             'Wage': 72000,
                             'Attacking': 75.2,
                             'Movement': 84.4,
                             'Defending': 32.7,
                             'Goalkeeping': 10.8,
                             'Overall rating': 86,
                             'Position': 'RM',
                             'Height': 179,
                             'Preferred foot': 'Left'},
                     'q26': 'M. de Ligt',
                     'q27': 86,
                     'q28': {239085: 78.6,
                             231747: 83.0,
                             192985: 82.4,
                             202126: 88.0,
                             192119: 17.2,
                             188545: 86.6,
                             165153: 86.6,
                             158023: 81.8,
                             239818: 57.0,
                             238794: 73.8,
                             231866: 71.2,
                             209331: 79.8,
                             203376: 63.0,
                             200145: 74.6,
                             190871: 80.0,
                             212622: 77.6,
                             212198: 82.6,
                             230621: 16.0,
                             228702: 76.6,
                             222665: 78.2,
                             200104: 80.2,
                             193080: 20.6,
                             177003: 76.0,
                             256790: 69.4,
                             253163: 62.6,
                             252371: 74.8,
                             251854: 66.8,
                             246669: 74.2,
                             235243: 62.2,
                             232411: 76.6,
                             231443: 72.0,
                             215914: 64.4,
                             211110: 79.8,
                             208722: 80.0,
                             204485: 75.2,
                             199556: 70.6,
                             186942: 74.4,
                             182521: 79.2,
                             20801: 81.8,
                             237692: 70.2,
                             268965: 41.4,
                             268963: 41.2,
                             268280: 35.6,
                             262760: 40.0,
                             258802: 41.4,
                             269032: 41.0,
                             269038: 37.2,
                             268279: 38.0,
                             213799: 29.4,
                             183162: 26.2},
                     'q29': {'E. Haaland': 239085,
                             'K. Mbappé': 231747,
                             'K. De Bruyne': 192985,
                             'H. Kane': 202126,
                             'T. Courtois': 192119,
                             'R. Lewandowski': 188545,
                             'K. Benzema': 165153,
                             'L. Messi': 158023,
                             'Rúben Dias': 239818,
                             'Vini Jr.': 238794,
                             'Rodri': 231866,
                             'M. Salah': 209331,
                             'V. van Dijk': 203376,
                             'Casemiro': 200145,
                             'Neymar Jr': 190871,
                             'J. Kimmich': 212622,
                             'Bruno Fernandes': 212198,
                             'G. Donnarumma': 230621,
                             'F. de Jong': 228702,
                             'M. Ødegaard': 222665,
                             'H. Son': 200104,
                             'De Gea': 193080,
                             'L. Modrić': 177003,
                             'J. Musiala': 256790,
                             'R. Araujo': 253163,
                             'J. Bellingham': 252371,
                             'Pedri': 251854,
                             'B. Saka': 246669,
                             'M. de Ligt': 235243,
                             'C. Nkunku': 232411,
                             'O. Dembélé': 231443,
                             'N. Kanté': 215914,
                             'P. Dybala': 211110,
                             'S. Mané': 208722,
                             'R. Mahrez': 204485,
                             'M. Verratti': 199556,
                             'İ. Gündoğan': 186942,
                             'T. Kroos': 182521,
                             'Cristiano Ronaldo': 20801,
                             'P. Foden': 237692,
                             'Zhang Yujun': 268965,
                             'Zhang Jiahui': 268963,
                             'L. Hebbelmann': 268280,
                             'N. Logue': 262760,
                             'B. Singh': 258802,
                             'Chen Zeshi': 269032,
                             'Zhang Wenxuan': 269038,
                             'J. Looschen': 268279,
                             'H. McFadden': 213799,
                             'J. Yates': 183162},
                     'q30': {'E. Haaland': 'Norway',
                             'K. Mbappé': 'France',
                             'K. De Bruyne': 'Belgium',
                             'H. Kane': 'England',
                             'T. Courtois': 'Belgium',
                             'R. Lewandowski': 'Poland',
                             'K. Benzema': 'France',
                             'L. Messi': 'Argentina',
                             'Rúben Dias': 'Portugal',
                             'Vini Jr.': 'Brazil',
                             'Rodri': 'Spain',
                             'M. Salah': 'Egypt',
                             'V. van Dijk': 'Netherlands',
                             'Casemiro': 'Brazil',
                             'Neymar Jr': 'Brazil',
                             'J. Kimmich': 'Germany',
                             'Bruno Fernandes': 'Portugal',
                             'G. Donnarumma': 'Italy',
                             'F. de Jong': 'Netherlands',
                             'M. Ødegaard': 'Norway',
                             'H. Son': 'Korea Republic',
                             'De Gea': 'Spain',
                             'L. Modrić': 'Croatia',
                             'J. Musiala': 'Germany',
                             'R. Araujo': 'Uruguay',
                             'J. Bellingham': 'England',
                             'Pedri': 'Spain',
                             'B. Saka': 'England',
                             'M. de Ligt': 'Netherlands',
                             'C. Nkunku': 'France',
                             'O. Dembélé': 'France',
                             'N. Kanté': 'France',
                             'P. Dybala': 'Argentina',
                             'S. Mané': 'Senegal',
                             'R. Mahrez': 'Algeria',
                             'M. Verratti': 'Italy',
                             'İ. Gündoğan': 'Germany',
                             'T. Kroos': 'Germany',
                             'Cristiano Ronaldo': 'Portugal',
                             'P. Foden': 'England',
                             'Zhang Yujun': 'China PR',
                             'Zhang Jiahui': 'China PR',
                             'L. Hebbelmann': 'Germany',
                             'N. Logue': 'Republic of Ireland',
                             'B. Singh': 'India',
                             'Chen Zeshi': 'China PR',
                             'Zhang Wenxuan': 'China PR',
                             'J. Looschen': 'Germany',
                             'H. McFadden': 'Republic of Ireland',
                             'J. Yates': 'England'},
                     'q31': {'Left': 10, 'Right': 40},
                     'q32': {'Norway': 2,
                             'France': 5,
                             'Belgium': 2,
                             'England': 5,
                             'Poland': 1,
                             'Argentina': 2,
                             'Portugal': 3,
                             'Brazil': 3,
                             'Spain': 3,
                             'Egypt': 1,
                             'Netherlands': 3,
                             'Germany': 6,
                             'Italy': 2,
                             'Korea Republic': 1,
                             'Croatia': 1,
                             'Uruguay': 1,
                             'Senegal': 1,
                             'Algeria': 1,
                             'China PR': 4,
                             'Republic of Ireland': 2,
                             'India': 1},
                     'q33': {'Norway': 156.8,
                             'France': 382.6,
                             'Belgium': 99.60000000000001,
                             'England': 333.4,
                             'Poland': 86.6,
                             'Argentina': 161.6,
                             'Portugal': 221.39999999999998,
                             'Brazil': 228.39999999999998,
                             'Spain': 158.60000000000002,
                             'Egypt': 79.8,
                             'Netherlands': 201.8,
                             'Germany': 374.20000000000005,
                             'Italy': 86.6,
                             'Korea Republic': 80.2,
                             'Croatia': 76.0,
                             'Uruguay': 62.6,
                             'Senegal': 80.0,
                             'Algeria': 75.2,
                             'China PR': 160.8,
                             'Republic of Ireland': 69.4,
                             'India': 41.4},
                     'q34': {'E. Haaland': 83.6,
                             'K. Mbappé': 92.4,
                             'K. De Bruyne': 77.6,
                             'H. Kane': 74.0,
                             'T. Courtois': 58.0,
                             'R. Lewandowski': 80.8,
                             'K. Benzema': 79.6,
                             'L. Messi': 87.0,
                             'Rúben Dias': 65.6,
                             'Vini Jr.': 90.8,
                             'Rodri': 66.8,
                             'M. Salah': 90.2,
                             'V. van Dijk': 70.0,
                             'Casemiro': 67.4,
                             'Neymar Jr': 87.4,
                             'J. Kimmich': 79.6,
                             'Bruno Fernandes': 78.0,
                             'G. Donnarumma': 58.2,
                             'F. de Jong': 83.4,
                             'M. Ødegaard': 79.2,
                             'H. Son': 83.4,
                             'De Gea': 56.4,
                             'L. Modrić': 82.8,
                             'J. Musiala': 88.2,
                             'R. Araujo': 70.2,
                             'J. Bellingham': 79.8,
                             'Pedri': 84.4,
                             'B. Saka': 85.2,
                             'M. de Ligt': 66.4,
                             'C. Nkunku': 85.2,
                             'O. Dembélé': 87.6,
                             'N. Kanté': 79.4,
                             'P. Dybala': 83.4,
                             'S. Mané': 86.4,
                             'R. Mahrez': 84.4,
                             'M. Verratti': 76.6,
                             'İ. Gündoğan': 73.0,
                             'T. Kroos': 65.0,
                             'Cristiano Ronaldo': 75.4,
                             'P. Foden': 85.2,
                             'Zhang Yujun': 55.8,
                             'Zhang Jiahui': 54.0,
                             'L. Hebbelmann': 38.4,
                             'N. Logue': 49.8,
                             'B. Singh': 58.2,
                             'Chen Zeshi': 60.4,
                             'Zhang Wenxuan': 61.0,
                             'J. Looschen': 36.6,
                             'H. McFadden': 45.0,
                             'J. Yates': 34.0},
                     'q35': {'E. Haaland': 78.6,
                             'K. Mbappé': 83.0,
                             'K. De Bruyne': 82.4,
                             'H. Kane': 88.0,
                             'T. Courtois': 17.2,
                             'R. Lewandowski': 86.6,
                             'K. Benzema': 86.6,
                             'L. Messi': 81.8,
                             'Rúben Dias': 57.0,
                             'Vini Jr.': 73.8,
                             'Rodri': 71.2,
                             'M. Salah': 79.8,
                             'V. van Dijk': 63.0,
                             'Casemiro': 74.6,
                             'Neymar Jr': 80.0,
                             'J. Kimmich': 77.6,
                             'Bruno Fernandes': 82.6,
                             'G. Donnarumma': 16.0,
                             'F. de Jong': 76.6,
                             'M. Ødegaard': 78.2,
                             'H. Son': 80.2,
                             'De Gea': 20.6,
                             'L. Modrić': 76.0,
                             'J. Musiala': 69.4,
                             'R. Araujo': 62.6,
                             'J. Bellingham': 74.8,
                             'Pedri': 66.8,
                             'B. Saka': 74.2,
                             'M. de Ligt': 62.2,
                             'C. Nkunku': 76.6,
                             'O. Dembélé': 72.0,
                             'N. Kanté': 64.4,
                             'P. Dybala': 79.8,
                             'S. Mané': 80.0,
                             'R. Mahrez': 75.2,
                             'M. Verratti': 70.6,
                             'İ. Gündoğan': 74.4,
                             'T. Kroos': 79.2,
                             'Cristiano Ronaldo': 81.8,
                             'P. Foden': 70.2,
                             'Zhang Yujun': 41.4,
                             'Zhang Jiahui': 41.2,
                             'L. Hebbelmann': 35.6,
                             'N. Logue': 40.0,
                             'B. Singh': 41.4,
                             'Chen Zeshi': 41.0,
                             'Zhang Wenxuan': 37.2,
                             'J. Looschen': 38.0,
                             'H. McFadden': 29.4,
                             'J. Yates': 26.2},
                     'q36': {'E. Haaland': 162.2,
                             'K. Mbappé': 175.4,
                             'K. De Bruyne': 160.0,
                             'H. Kane': 162.0,
                             'T. Courtois': 75.2,
                             'R. Lewandowski': 167.39999999999998,
                             'K. Benzema': 166.2,
                             'L. Messi': 168.8,
                             'Rúben Dias': 122.6,
                             'Vini Jr.': 164.6,
                             'Rodri': 138.0,
                             'M. Salah': 170.0,
                             'V. van Dijk': 133.0,
                             'Casemiro': 142.0,
                             'Neymar Jr': 167.4,
                             'J. Kimmich': 157.2,
                             'Bruno Fernandes': 160.6,
                             'G. Donnarumma': 74.2,
                             'F. de Jong': 160.0,
                             'M. Ødegaard': 157.4,
                             'H. Son': 163.60000000000002,
                             'De Gea': 77.0,
                             'L. Modrić': 158.8,
                             'J. Musiala': 157.60000000000002,
                             'R. Araujo': 132.8,
                             'J. Bellingham': 154.6,
                             'Pedri': 151.2,
                             'B. Saka': 159.4,
                             'M. de Ligt': 128.60000000000002,
                             'C. Nkunku': 161.8,
                             'O. Dembélé': 159.6,
                             'N. Kanté': 143.8,
                             'P. Dybala': 163.2,
                             'S. Mané': 166.4,
                             'R. Mahrez': 159.60000000000002,
                             'M. Verratti': 147.2,
                             'İ. Gündoğan': 147.4,
                             'T. Kroos': 144.2,
                             'Cristiano Ronaldo': 157.2,
                             'P. Foden': 155.4,
                             'Zhang Yujun': 97.19999999999999,
                             'Zhang Jiahui': 95.2,
                             'L. Hebbelmann': 74.0,
                             'N. Logue': 89.8,
                             'B. Singh': 99.6,
                             'Chen Zeshi': 101.4,
                             'Zhang Wenxuan': 98.2,
                             'J. Looschen': 74.6,
                             'H. McFadden': 74.4,
                             'J. Yates': 60.2},
                     'q37': {'Norway': 78.4,
                             'France': 76.52000000000001,
                             'Belgium': 49.800000000000004,
                             'England': 66.67999999999999,
                             'Poland': 86.6,
                             'Argentina': 80.8,
                             'Portugal': 73.8,
                             'Brazil': 76.13333333333333,
                             'Spain': 52.866666666666674,
                             'Egypt': 79.8,
                             'Netherlands': 67.26666666666667,
                             'Germany': 62.366666666666674,
                             'Italy': 43.3,
                             'Korea Republic': 80.2,
                             'Croatia': 76.0,
                             'Uruguay': 62.6,
                             'Senegal': 80.0,
                             'Algeria': 75.2,
                             'China PR': 40.2,
                             'Republic of Ireland': 34.7,
                             'India': 41.4},
                     'q38': 'Poland',
                     'q39': 'H. Kane',
                     'q40': 'J. Yates'}
    return expected_json


def get_special_json():
    """get_special_json() returns a dict mapping each question to the expected
    answer stored in a special format as a list of tuples. Each tuple contains
    the element expected in the list, and its corresponding value. Any two
    elements with the same value can appear in any order in the actual list,
    but if two elements have different values, then they must appear in the
    same order as in the expected list of tuples."""
    special_json = {}
    return special_json


def compare(expected, actual, q_format=TEXT_FORMAT):
    """compare(expected, actual) is used to compare when the format of
    the expected answer is known for certain."""
    try:
        if q_format == TEXT_FORMAT:
            return simple_compare(expected, actual)
        elif q_format == TEXT_FORMAT_UNORDERED_LIST:
            return list_compare_unordered(expected, actual)
        elif q_format == TEXT_FORMAT_ORDERED_LIST:
            return list_compare_ordered(expected, actual)
        elif q_format == TEXT_FORMAT_DICT:
            return dict_compare(expected, actual)
        elif q_format == TEXT_FORMAT_SPECIAL_ORDERED_LIST:
            return list_compare_special(expected, actual)
        elif q_format == TEXT_FORMAT_NAMEDTUPLE:
            return namedtuple_compare(expected, actual)
        elif q_format == PNG_FORMAT_SCATTER:
            return compare_flip_dicts(expected, actual)
        elif q_format == HTML_FORMAT:
            return compare_cell_html(expected, actual)
        elif q_format == FILE_JSON_FORMAT:
            return compare_json(expected, actual)
        else:
            if expected != actual:
                return "expected %s but found %s " % (repr(expected), repr(actual))
    except:
        if expected != actual:
            return "expected %s" % (repr(expected))
    return PASS


def print_message(expected, actual, complete_msg=True):
    """print_message(expected, actual) displays a simple error message."""
    msg = "expected %s" % (repr(expected))
    if complete_msg:
        msg = msg + " but found %s" % (repr(actual))
    return msg


def simple_compare(expected, actual, complete_msg=True):
    """simple_compare(expected, actual) is used to compare when the expected answer
    is a type/Nones/str/int/float/bool. When the expected answer is a float,
    the actual answer is allowed to be within the tolerance limit. Otherwise,
    the values must match exactly, or a very simple error message is displayed."""
    msg = PASS
    if 'numpy' in repr(type((actual))):
        actual = actual.item()
    if isinstance(expected, type):
        if expected != actual:
            if isinstance(actual, type):
                msg = "expected %s but found %s" % (expected.__name__, actual.__name__)
            else:
                msg = "expected %s but found %s" % (expected.__name__, repr(actual))
    elif not isinstance(actual, type(expected)) and not (isinstance(expected, (float, int)) and isinstance(actual, (float, int))):
        msg = "expected to find type %s but found type %s" % (type(expected).__name__, type(actual).__name__)
    elif isinstance(expected, float):
        if not math.isclose(actual, expected, rel_tol=REL_TOL, abs_tol=ABS_TOL):
            msg = print_message(expected, actual, complete_msg)
    elif isinstance(expected, (list, tuple)) or is_namedtuple(expected):
        new_msg = print_message(expected, actual, complete_msg)
        if len(expected) != len(actual):
            return new_msg
        for i in range(len(expected)):
            val = simple_compare(expected[i], actual[i])
            if val != PASS:
                return new_msg
    elif isinstance(expected, dict):
        new_msg = print_message(expected, actual, complete_msg)
        if len(expected) != len(actual):
            return new_msg
        val = simple_compare(list(expected.keys()), list(actual.keys()))
        if val != PASS:
            return new_msg
        for key in expected:
            val = simple_compare(expected[key], actual[key])
            if val != PASS:
                return new_msg
    else:
        if expected != actual:
            msg = print_message(expected, actual, complete_msg)
    return msg


def intelligent_compare(expected, actual, obj=None):
    """intelligent_compare(expected, actual) is used to compare when the
    data type of the expected answer is not known for certain, and default
    assumptions  need to be made."""
    if obj == None:
        obj = type(expected).__name__
    if is_namedtuple(expected):
        msg = namedtuple_compare(expected, actual)
    elif isinstance(expected, (list, tuple)):
        msg = list_compare_ordered(expected, actual, obj)
    elif isinstance(expected, set):
        msg = list_compare_unordered(expected, actual, obj)
    elif isinstance(expected, (dict)):
        msg = dict_compare(expected, actual)
    else:
        msg = simple_compare(expected, actual)
    msg = msg.replace("CompDict", "dict").replace("CompSet", "set").replace("NewNone", "None")
    return msg


def is_namedtuple(obj, init_check=True):
    """is_namedtuple(obj) returns True if `obj` is a namedtuple object
    defined in the test file."""
    bases = type(obj).__bases__
    if len(bases) != 1 or bases[0] != tuple:
        return False
    fields = getattr(type(obj), '_fields', None)
    if not isinstance(fields, tuple):
        return False
    if init_check and not type(obj).__name__ in [nt.__name__ for nt in _expected_namedtuples]:
        return False
    return True


def list_compare_ordered(expected, actual, obj=None):
    """list_compare_ordered(expected, actual) is used to compare when the
    expected answer is a list/tuple, where the order of the elements matters."""
    msg = PASS
    if not isinstance(actual, type(expected)):
        msg = "expected to find type %s but found type %s" % (type(expected).__name__, type(actual).__name__)
        return msg
    if obj == None:
        obj = type(expected).__name__
    for i in range(len(expected)):
        if i >= len(actual):
            msg = "at index %d of the %s, expected missing %s" % (i, obj, repr(expected[i]))
            break
        val = intelligent_compare(expected[i], actual[i], "sub" + obj)
        if val != PASS:
            msg = "at index %d of the %s, " % (i, obj) + val
            break
    if len(actual) > len(expected) and msg == PASS:
        msg = "at index %d of the %s, found unexpected %s" % (len(expected), obj, repr(actual[len(expected)]))
    if len(expected) != len(actual):
        msg = msg + " (found %d entries in %s, but expected %d)" % (len(actual), obj, len(expected))

    if len(expected) > 0:
        try:
            if msg != PASS and list_compare_unordered(expected, actual, obj) == PASS:
                msg = msg + " (%s may not be ordered as required)" % (obj)
        except:
            pass
    return msg


def list_compare_helper(larger, smaller):
    """list_compare_helper(larger, smaller) is a helper function which takes in
    two lists of possibly unequal sizes and finds the item that is not present
    in the smaller list, if there is such an element."""
    msg = PASS
    j = 0
    for i in range(len(larger)):
        if i == len(smaller):
            msg = "expected %s" % (repr(larger[i]))
            break
        found = False
        while not found:
            if j == len(smaller):
                val = simple_compare(larger[i], smaller[j - 1], complete_msg=False)
                break
            val = simple_compare(larger[i], smaller[j], complete_msg=False)
            j += 1
            if val == PASS:
                found = True
                break
        if not found:
            msg = val
            break
    return msg

class NewNone():
    """alternate class in place of None, which allows for comparison with
    all other data types."""
    def __str__(self):
        return 'None'
    def __repr__(self):
        return 'None'
    def __lt__(self, other):
        return True
    def __le__(self, other):
        return True
    def __gt__(self, other):
        return False
    def __ge__(self, other):
        return other == None
    def __eq__(self, other):
        return other == None
    def __ne__(self, other):
        return other != None

class CompDict(dict):
    """subclass of dict, which allows for comparison with other dicts."""
    def __init__(self, vals):
        super(self.__class__, self).__init__(vals)
        if type(vals) == CompDict:
            self.val = vals.val
        elif isinstance(vals, dict):
            self.val = self.get_equiv(vals)
        else:
            raise TypeError("'%s' object cannot be type casted to CompDict class" % type(vals).__name__)

    def get_equiv(self, vals):
        val = []
        for key in sorted(list(vals.keys())):
            val.append((key, vals[key]))
        return val

    def __str__(self):
        return str(dict(self.val))
    def __repr__(self):
        return repr(dict(self.val))
    def __lt__(self, other):
        return self.val < CompDict(other).val
    def __le__(self, other):
        return self.val <= CompDict(other).val
    def __gt__(self, other):
        return self.val > CompDict(other).val
    def __ge__(self, other):
        return self.val >= CompDict(other).val
    def __eq__(self, other):
        return self.val == CompDict(other).val
    def __ne__(self, other):
        return self.val != CompDict(other).val

class CompSet(set):
    """subclass of set, which allows for comparison with other sets."""
    def __init__(self, vals):
        super(self.__class__, self).__init__(vals)
        if type(vals) == CompSet:
            self.val = vals.val
        elif isinstance(vals, set):
            self.val = self.get_equiv(vals)
        else:
            raise TypeError("'%s' object cannot be type casted to CompSet class" % type(vals).__name__)

    def get_equiv(self, vals):
        return sorted(list(vals))

    def __str__(self):
        return str(set(self.val))
    def __repr__(self):
        return repr(set(self.val))
    def __getitem__(self, index):
        return self.val[index]
    def __lt__(self, other):
        return self.val < CompSet(other).val
    def __le__(self, other):
        return self.val <= CompSet(other).val
    def __gt__(self, other):
        return self.val > CompSet(other).val
    def __ge__(self, other):
        return self.val >= CompSet(other).val
    def __eq__(self, other):
        return self.val == CompSet(other).val
    def __ne__(self, other):
        return self.val != CompSet(other).val

def make_sortable(item):
    """make_sortable(item) replaces all Nones in `item` with an alternate
    class that allows for comparison with str/int/float/bool/list/set/tuple/dict.
    It also replaces all dicts (and sets) with a subclass that allows for
    comparison with other dicts (and sets)."""
    if item == None:
        return NewNone()
    elif isinstance(item, (type, str, int, float, bool)):
        return item
    elif isinstance(item, (list, set, tuple)):
        new_item = []
        for subitem in item:
            new_item.append(make_sortable(subitem))
        if is_namedtuple(item):
            return type(item)(*new_item)
        elif isinstance(item, set):
            return CompSet(new_item)
        else:
            return type(item)(new_item)
    elif isinstance(item, dict):
        new_item = {}
        for key in item:
            new_item[key] = make_sortable(item[key])
        return CompDict(new_item)
    return item

def list_compare_unordered(expected, actual, obj=None):
    """list_compare_unordered(expected, actual) is used to compare when the
    expected answer is a list/set where the order of the elements does not matter."""
    msg = PASS
    if not isinstance(actual, type(expected)):
        msg = "expected to find type %s but found type %s" % (type(expected).__name__, type(actual).__name__)
        return msg
    if obj == None:
        obj = type(expected).__name__

    try:
        sort_expected = sorted(make_sortable(expected))
        sort_actual = sorted(make_sortable(actual))
    except:
        return "unexpected datatype found in %s; expected entries of type %s" % (obj, obj, type(expected[0]).__name__)

    if len(actual) == 0 and len(expected) > 0:
        msg = "in the %s, missing" % (obj) + sort_expected[0]
    elif len(actual) > 0 and len(expected) > 0:
        val = intelligent_compare(sort_expected[0], sort_actual[0])
        if val.startswith("expected to find type"):
            msg = "in the %s, " % (obj) + simple_compare(sort_expected[0], sort_actual[0])
        else:
            if len(expected) > len(actual):
                msg = "in the %s, missing " % (obj) + list_compare_helper(sort_expected, sort_actual)
            elif len(expected) < len(actual):
                msg = "in the %s, found un" % (obj) + list_compare_helper(sort_actual, sort_expected)
            if len(expected) != len(actual):
                msg = msg + " (found %d entries in %s, but expected %d)" % (len(actual), obj, len(expected))
                return msg
            else:
                val = list_compare_helper(sort_expected, sort_actual)
                if val != PASS:
                    msg = "in the %s, missing " % (obj) + val + ", but found un" + list_compare_helper(sort_actual,
                                                                                               sort_expected)
    return msg


def namedtuple_compare(expected, actual):
    """namedtuple_compare(expected, actual) is used to compare when the
    expected answer is a namedtuple defined in the test file."""
    msg = PASS
    if is_namedtuple(actual, False):
        msg = "expected namedtuple but found %s" % (type(actual).__name__)
        return msg
    if type(expected).__name__ != type(actual).__name__:
        return "expected namedtuple %s but found namedtuple %s" % (type(expected).__name__, type(actual).__name__)
    expected_fields = expected._fields
    actual_fields = actual._fields
    msg = list_compare_ordered(list(expected_fields), list(actual_fields), "namedtuple attributes")
    if msg != PASS:
        return msg
    for field in expected_fields:
        val = intelligent_compare(getattr(expected, field), getattr(actual, field))
        if val != PASS:
            msg = "at attribute %s of namedtuple %s, " % (field, type(expected).__name__) + val
            return msg
    return msg


def clean_slashes(item):
    """clean_slashes()"""
    if isinstance(item, str):
        return item.replace("\\", "/").replace("/", os.path.sep)
    elif item == None or isinstance(item, (type, int, float, bool)):
        return item
    elif isinstance(item, (list, tuple, set)) or is_namedtuple(item):
        new_item = []
        for subitem in item:
            new_item.append(clean_slashes(subitem))
        if is_namedtuple(item):
            return type(item)(*new_item)
        else:
            return type(item)(new_item)
    elif isinstance(item, dict):
        new_item = {}
        for key in item:
            new_item[clean_slashes(key)] = clean_slashes(item[key])
        return item


def list_compare_special_initialize(special_expected):
    """list_compare_special_initialize(special_expected) takes in the special
    ordering stored as a sorted list of items, and returns a list of lists
    where the ordering among the inner lists does not matter."""
    latest_val = None
    clean_special = []
    for row in special_expected:
        if latest_val == None or row[1] != latest_val:
            clean_special.append([])
            latest_val = row[1]
        clean_special[-1].append(row[0])
    return clean_special


def list_compare_special(special_expected, actual):
    """list_compare_special(special_expected, actual) is used to compare when the
    expected answer is a list with special ordering defined in `special_expected`."""
    msg = PASS
    expected_list = []
    special_order = list_compare_special_initialize(special_expected)
    for expected_item in special_order:
        expected_list.extend(expected_item)
    val = list_compare_unordered(expected_list, actual)
    if val != PASS:
        return val
    i = 0
    for expected_item in special_order:
        j = len(expected_item)
        actual_item = actual[i: i + j]
        val = list_compare_unordered(expected_item, actual_item)
        if val != PASS:
            if j == 1:
                msg = "at index %d " % (i) + val
            else:
                msg = "between indices %d and %d " % (i, i + j - 1) + val
            msg = msg + " (list may not be ordered as required)"
            break
        i += j
    return msg


def dict_compare(expected, actual, obj=None):
    """dict_compare(expected, actual) is used to compare when the expected answer
    is a dict."""
    msg = PASS
    if not isinstance(actual, type(expected)):
        msg = "expected to find type %s but found type %s" % (type(expected).__name__, type(actual).__name__)
        return msg
    if obj == None:
        obj = type(expected).__name__

    expected_keys = list(expected.keys())
    actual_keys = list(actual.keys())
    val = list_compare_unordered(expected_keys, actual_keys, obj)

    if val != PASS:
        msg = "bad keys in %s: " % (obj) + val
    if msg == PASS:
        for key in expected:
            new_obj = None
            if isinstance(expected[key], (list, tuple, set)):
                new_obj = 'value'
            elif isinstance(expected[key], dict):
                new_obj = 'sub' + obj
            val = intelligent_compare(expected[key], actual[key], new_obj)
            if val != PASS:
                msg = "incorrect value for key %s in %s: " % (repr(key), obj) + val
    return msg


def is_flippable(item):
    """is_flippable(item) determines if the given dict of lists has lists of the
    same length and is therefore flippable."""
    item_lens = set(([str(len(item[key])) for key in item]))
    if len(item_lens) == 1:
        return PASS
    else:
        return "found lists of lengths %s" % (", ".join(list(item_lens)))

def flip_dict_of_lists(item):
    """flip_dict_of_lists(item) flips a dict of lists into a list of dicts if the
    lists are of same length."""
    new_item = []
    length = len(list(item.values())[0])
    for i in range(length):
        new_dict = {}
        for key in item:
            new_dict[key] = item[key][i]
        new_item.append(new_dict)
    return new_item

def compare_flip_dicts(expected, actual, obj="lists"):
    """compare_flip_dicts(expected, actual) flips a dict of lists (or dicts) into
    a list of dicts (or dict of dicts) and then compares the list ignoring order."""
    msg = PASS
    example_item = list(expected.values())[0]
    if isinstance(example_item, (list, tuple)):
        val = is_flippable(actual)
        if val != PASS:
            msg = "expected to find lists of length %d, but " % (len(example_item)) + val
            return msg
        msg = list_compare_unordered(flip_dict_of_lists(expected), flip_dict_of_lists(actual), "lists")
    elif isinstance(example_item, dict):
        expected_keys = list(example_item.keys())
        for key in actual:
            val = list_compare_unordered(expected_keys, list(actual[key].keys()), "dictionary %s" % key)
            if val != PASS:
                return val
        for cat_key in expected_keys:
            expected_category = {}
            actual_category = {}
            for key in expected:
                expected_category[key] = expected[key][cat_key]
                actual_category[key] = actual[key][cat_key]
            val = list_compare_unordered(flip_dict_of_lists(expected), flip_dict_of_lists(actual), "category " + repr(cat_key))
            if val != PASS:
                return val
    return msg


def get_expected_tables():
    """get_expected_tables() reads the html file with the expected DataFrames
    and returns a dict mapping each question to a html table."""
    if not os.path.exists(DF_FILE):
        return None

    expected_tables = {}
    f = open(DF_FILE, encoding='utf-8')
    soup = BeautifulSoup(f.read(), 'html.parser')
    f.close()

    tables = soup.find_all('table')
    for table in tables:
        expected_tables[table.get("data-question")] = table

    return expected_tables

def parse_df_html_table(table):
    """parse_df_html_table(table) takes in a table as a html string and returns
    a dict mapping each row and column index to the value at that position."""
    rows = []
    for tr in table.find_all('tr'):
        rows.append([])
        for cell in tr.find_all(['td', 'th']):
            rows[-1].append(cell.get_text().strip("\n "))

    cells = {}
    for r in range(1, len(rows)):
        for c in range(1, len(rows[0])):
            rname = rows[r][0]
            cname = rows[0][c]
            cells[(rname,cname)] = rows[r][c]
    return cells


def get_expected_namedtuples():
    """get_expected_namedtuples() defines the required namedtuple objects
    globally. It also returns a tuple of the classes."""
    expected_namedtuples = []
    
    return tuple(expected_namedtuples)

_expected_namedtuples = get_expected_namedtuples()


def compare_cell_html(expected, actual):
    """compare_cell_html(expected, actual) is used to compare when the
    expected answer is a DataFrame stored in the `expected_dfs` html file."""
    expected_cells = parse_df_html_table(expected)
    try:
        actual_cells = parse_df_html_table(BeautifulSoup(actual, 'html.parser').find('table'))
    except Exception as e:
        return "expected to find type DataFrame but found type %s instead" % type(actual).__name__

    expected_cols = list(set(["column %s" % (loc[1]) for loc in expected_cells]))
    actual_cols = list(set(["column %s" % (loc[1]) for loc in actual_cells]))
    msg = list_compare_unordered(expected_cols, actual_cols, "DataFrame")
    if msg != PASS:
        return msg

    expected_rows = list(set(["row index %s" % (loc[0]) for loc in expected_cells]))
    actual_rows = list(set(["row index %s" % (loc[0]) for loc in actual_cells]))
    msg = list_compare_unordered(expected_rows, actual_rows, "DataFrame")
    if msg != PASS:
        return msg

    for location, expected in expected_cells.items():
        location_name = "column {} at index {}".format(location[1], location[0])
        actual = actual_cells.get(location, None)
        if actual == None:
            return "in %s, expected to find %s" % (location_name, repr(expected))
        try:
            actual_ans = float(actual)
            expected_ans = float(expected)
            if math.isnan(actual_ans) and math.isnan(expected_ans):
                continue
        except Exception as e:
            actual_ans, expected_ans = actual, expected
        msg = simple_compare(expected_ans, actual_ans)
        if msg != PASS:
            return "in %s, " % location_name + msg
    return PASS


def get_expected_plots():
    """get_expected_plots() reads the json file with the expected plot data
    and returns a dict mapping each question to a dictionary with the plots data."""
    if not os.path.exists(PLOT_FILE):
        return None

    f = open(PLOT_FILE, encoding='utf-8')
    expected_plots = json.load(f)
    f.close()
    return expected_plots


def compare_file_json(expected, actual):
    """compare_file_json(expected, actual) is used to compare when the
    expected answer is a JSON file."""
    msg = PASS
    if not os.path.isfile(expected):
        return "file %s not found; make sure it is downloaded and stored in the correct directory" % (expected)
    elif not os.path.isfile(actual):
        return "file %s not found; make sure that you have created the file with the correct name" % (actual)
    try:
        e = open(expected, encoding='utf-8')
        expected_data = json.load(e)
        e.close()
    except json.JSONDecodeError:
        return "file %s is broken and cannot be parsed; please delete and redownload the file correctly" % (expected)
    try:
        a = open(actual, encoding='utf-8')
        actual_data = json.load(a)
        a.close()
    except json.JSONDecodeError:
        return "file %s is broken and cannot be parsed" % (actual)
    if type(expected_data) == list:
        msg = list_compare_ordered(expected_data, actual_data, 'file ' + actual)
    elif type(expected_data) == dict:
        msg = dict_compare(expected_data, actual_data)
    return msg


_expected_json = get_expected_json()
_special_json = get_special_json()
_expected_plots = get_expected_plots()
_expected_tables = get_expected_tables()
_expected_format = get_expected_format()

def check(qnum, actual):
    """check(qnum, actual) is used to check if the answer in the notebook is
    the correct answer, and provide useful feedback if the answer is incorrect."""
    msg = PASS
    error_msg = "<b style='color: red;'>ERROR:</b> "
    q_format = _expected_format[qnum]

    if q_format == TEXT_FORMAT_SPECIAL_ORDERED_LIST:
        expected = _special_json[qnum]
    elif q_format == PNG_FORMAT_SCATTER:
        if _expected_plots == None:
            msg = error_msg + "file %s not parsed; make sure it is downloaded and stored in the correct directory" % (PLOT_FILE)
        else:
            expected = _expected_plots[qnum]
    elif q_format == HTML_FORMAT:
        if _expected_tables == None:
            msg = error_msg + "file %s not parsed; make sure it is downloaded and stored in the correct directory" % (DF_FILE)
        else:
            expected = _expected_tables[qnum]
    else:
        expected = _expected_json[qnum]

    if SLASHES in q_format:
        q_format = q_format.replace(SLASHES, "")
        expected = clean_slashes(expected)
        actual = clean_slashes(actual)

    if msg != PASS:
        print(msg)
    else:
        msg = compare(expected, actual, q_format)
        if msg != PASS:
            msg = error_msg + msg
        print(msg)


def check_file_size(path):
    """check_file_size(path) throws an error if the file is too big to display
    on Gradescope."""
    size = os.path.getsize(path)
    assert size < MAX_FILE_SIZE * 10**3, "Your file is too big to be displayed by Gradescope; please delete unnecessary output cells so your file size is < %s KB" % MAX_FILE_SIZE


def reset_hidden_tests():
    """reset_hidden_tests() resets all hidden tests on the Gradescope autograder where the hidden test file exists"""
    if not os.path.exists(HIDDEN_FILE):
        return
    hidn.reset_hidden_tests()

def rubric_check(rubric_point, ignore_past_errors=True):
    """rubric_check(rubric_point) uses the hidden test file on the Gradescope autograder to grade the `rubric_point`"""
    if not os.path.exists(HIDDEN_FILE):
        print(PASS)
        return
    error_msg_1 = "ERROR: "
    error_msg_2 = "TEST DETAILS: "
    try:
        msg = hidn.rubric_check(rubric_point, ignore_past_errors)
    except:
        msg = "hidden tests crashed before execution"
    if msg != PASS:
        hidn.make_deductions(rubric_point)
        if msg == "public tests failed":
            comment = "The public tests have failed, so you will not receive any points for this question."
            comment += "\nPlease confirm that the public tests pass locally before submitting."
        elif msg == "answer is hardcoded":
            comment = "In the datasets for testing hardcoding, all numbers are replaced with random values."
            comment += "\nIf the answer is the same as in the original dataset for all these datasets"
            comment += "\ndespite this, that implies that the answer in the notebook is hardcoded."
            comment += "\nYou will not receive any points for this question."
        else:
            comment = hidn.get_comment(rubric_point)
        msg = error_msg_1 + msg
        if comment != "":
            msg = msg + "\n" + error_msg_2 + comment
    print(msg)

def get_summary():
    """get_summary() returns the summary of the notebook using the hidden test file on the Gradescope autograder"""
    if not os.path.exists(HIDDEN_FILE):
        print("Total Score: %d/%d" % (TOTAL_SCORE, TOTAL_SCORE))
        return
    score = min(TOTAL_SCORE, hidn.get_score(TOTAL_SCORE))
    display_msg = "Total Score: %d/%d" % (score, TOTAL_SCORE)
    if score != TOTAL_SCORE:
        display_msg += "\n" + hidn.get_deduction_string()
    print(display_msg)

def get_score_digit(digit):
    """get_score_digit(digit) returns the `digit` of the score using the hidden test file on the Gradescope autograder"""
    if not os.path.exists(HIDDEN_FILE):
        score = TOTAL_SCORE
    else:
        score = hidn.get_score(TOTAL_SCORE)
    digits = bin(score)[2:]
    digits = "0"*(7 - len(digits)) + digits
    return int(digits[6 - digit])