-
Anna Meyer authoredAnna Meyer authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
p10_test.py 25.04 KiB
#!/usr/bin/python
import os, json, math
from collections import namedtuple
MAX_FILE_SIZE = 300 # units - KB
REL_TOL = 6e-04 # relative tolerance for floats
ABS_TOL = 15e-03 # absolute tolerance for floats
PASS = "PASS"
TEXT_FORMAT = "text" # question type when expected answer is a str, int, float, or bool
TEXT_FORMAT_NAMEDTUPLE = "text namedtuple" # question type when expected answer is a namedtuple
TEXT_FORMAT_UNORDERED_LIST = "text list_unordered" # question type when the expected answer is a list where the order does *not* matter
TEXT_FORMAT_ORDERED_LIST = "text list_ordered" # question type when the expected answer is a list where the order does matter
TEXT_FORMAT_SPECIAL_ORDERED_LIST = "text list_special_ordered" # 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_DICT = "text dict" # question type when the expected answer is a dictionary
def return_expected_json():
expected_json = {"1": (TEXT_FORMAT_ORDERED_LIST, ['stars_5.csv',
'stars_4.csv',
'stars_3.csv',
'stars_2.csv',
'stars_1.csv',
'planets_5.csv',
'planets_4.csv',
'planets_3.csv',
'planets_2.csv',
'planets_1.csv',
'mapping_5.json',
'mapping_4.json',
'mapping_3.json',
'mapping_2.json',
'mapping_1.json']),
"2": (TEXT_FORMAT_ORDERED_LIST, [os.path.join("data", "stars_5.csv"),
os.path.join("data", "stars_4.csv"),
os.path.join("data", "stars_3.csv"),
os.path.join("data", "stars_2.csv"),
os.path.join("data", "stars_1.csv"),
os.path.join("data", "planets_5.csv"),
os.path.join("data", "planets_4.csv"),
os.path.join("data", "planets_3.csv"),
os.path.join("data", "planets_2.csv"),
os.path.join("data", "planets_1.csv"),
os.path.join("data", "mapping_5.json"),
os.path.join("data", "mapping_4.json"),
os.path.join("data", "mapping_3.json"),
os.path.join("data", "mapping_2.json"),
os.path.join("data", "mapping_1.json")]),
"3": (TEXT_FORMAT_ORDERED_LIST, [os.path.join("data", "stars_5.csv"),
os.path.join("data", "stars_4.csv"),
os.path.join("data", "stars_3.csv"),
os.path.join("data", "stars_2.csv"),
os.path.join("data", "stars_1.csv"),
os.path.join("data", "planets_5.csv"),
os.path.join("data", "planets_4.csv"),
os.path.join("data", "planets_3.csv"),
os.path.join("data", "planets_2.csv"),
os.path.join("data", "planets_1.csv")]),
"4": (TEXT_FORMAT_ORDERED_LIST, [os.path.join("data", "stars_5.csv"),
os.path.join("data", "stars_4.csv"),
os.path.join("data", "stars_3.csv"),
os.path.join("data", "stars_2.csv"),
os.path.join("data", "stars_1.csv")]),
"star_object": (TEXT_FORMAT_NAMEDTUPLE, Star(spectral_type='G2 V', stellar_effective_temperature=5780.0,
stellar_radius=1.0, stellar_mass=1.0, stellar_luminosity=0.0,
stellar_surface_gravity=4.44, stellar_age=4.6)),
"5": (TEXT_FORMAT_NAMEDTUPLE, Star(spectral_type='K0 III', stellar_effective_temperature=4813.0,
stellar_radius=11.0, stellar_mass=2.2, stellar_luminosity=1.763,
stellar_surface_gravity=2.63, stellar_age=4.5)),
"6": (TEXT_FORMAT_NAMEDTUPLE, Star(spectral_type=None, stellar_effective_temperature=13500.0,
stellar_radius=0.01, stellar_mass=0.69, stellar_luminosity=-2.4,
stellar_surface_gravity=None, stellar_age=None)),
"7": (TEXT_FORMAT, 0.016741496598639403),
"8": (TEXT_FORMAT, 4.290235955056181),
"9": (TEXT_FORMAT, 4632.0),
"10": (TEXT_FORMAT, 'HD 81817'),
"11": (TEXT_FORMAT, 4.217130505709651),
"planet_object": (TEXT_FORMAT_NAMEDTUPLE, Planet(planet_name='Jupiter', host_name='Sun',
discovery_method='Imaging', discovery_year=1610,
controversial_flag=False, orbital_period=4333.0,
planet_radius=11.209, planet_mass=317.828,
semi_major_radius=5.2038, eccentricity=0.0489,
equilibrium_temperature=110, insolation_flux=0.0345)),
"12": (TEXT_FORMAT_NAMEDTUPLE, Planet(planet_name='17 Sco b', host_name='17 Sco',
discovery_method='Radial Velocity', discovery_year=2020,
controversial_flag=False, orbital_period=578.38,
planet_radius=12.9, planet_mass=1373.01872, semi_major_radius=1.45,
eccentricity=0.06, equilibrium_temperature=None, insolation_flux=None)),
"13": (TEXT_FORMAT_ORDERED_LIST,[Planet(planet_name='Kepler-1478 b', host_name='Kepler-1478', discovery_method='Transit', discovery_year=2016, controversial_flag=False, orbital_period=26.0840594, planet_radius=1.73, planet_mass=3.64, semi_major_radius=0.1681, eccentricity=0.0, equilibrium_temperature=598.0, insolation_flux=85.28),
Planet(planet_name='Kepler-1479 b', host_name='Kepler-1479', discovery_method='Transit', discovery_year=2016, controversial_flag=False, orbital_period=14.53261362, planet_radius=1.83, planet_mass=4.01, semi_major_radius=0.1173, eccentricity=0.0, equilibrium_temperature=746.0, insolation_flux=98.57),
Planet(planet_name='Kepler-1480 b', host_name='Kepler-1480', discovery_method='Transit', discovery_year=2016, controversial_flag=False, orbital_period=22.12679948, planet_radius=1.67, planet_mass=3.43, semi_major_radius=0.1525, eccentricity=0.0, equilibrium_temperature=721.0, insolation_flux=16.32),
Planet(planet_name='Kepler-1481 b', host_name='Kepler-1481', discovery_method='Transit', discovery_year=2016, controversial_flag=False, orbital_period=5.94220998, planet_radius=1.23, planet_mass=2.04, semi_major_radius=0.059, eccentricity=0.0, equilibrium_temperature=797.0, insolation_flux=71.94),
Planet(planet_name='Kepler-1482 b', host_name='Kepler-1482', discovery_method='Transit', discovery_year=2016, controversial_flag=False, orbital_period=12.25383217, planet_radius=1.01, planet_mass=1.01, semi_major_radius=0.1016, eccentricity=0.0, equilibrium_temperature=678.0, insolation_flux=86.5)]),
"14": (TEXT_FORMAT_ORDERED_LIST, [Planet(planet_name='Kepler-452 b', host_name='Kepler-452', discovery_method='Transit', discovery_year=2015, controversial_flag=True, orbital_period=384.843, planet_radius=1.63, planet_mass=3.29, semi_major_radius=1.046, eccentricity=0.0, equilibrium_temperature=265.0, insolation_flux=1.1),
Planet(planet_name='Kepler-747 b', host_name='Kepler-747', discovery_method='Transit', discovery_year=2016, controversial_flag=True, orbital_period=35.61760587, planet_radius=5.27, planet_mass=24.1, semi_major_radius=0.1916, eccentricity=0.0, equilibrium_temperature=456.0, insolation_flux=10.19),
Planet(planet_name='V830 Tau b', host_name='V830 Tau', discovery_method='Radial Velocity', discovery_year=2016, controversial_flag=True, orbital_period=4.927, planet_radius=14.0, planet_mass=222.481, semi_major_radius=0.057, eccentricity=0.0, equilibrium_temperature=None, insolation_flux=None),
Planet(planet_name='nu Oct A b', host_name='nu Oct A', discovery_method='Radial Velocity', discovery_year=2016, controversial_flag=True, orbital_period=417.0, planet_radius=13.3, planet_mass=762.78818, semi_major_radius=1.25, eccentricity=0.11, equilibrium_temperature=None, insolation_flux=None)]),
"15": (TEXT_FORMAT_ORDERED_LIST, [Planet(planet_name='Wolf 1061 c', host_name='Wolf 1061', discovery_method='Radial Velocity', discovery_year=2015, controversial_flag=False, orbital_period=17.8719, planet_radius=1.66, planet_mass=3.41, semi_major_radius=0.089, eccentricity=0.11, equilibrium_temperature=None, insolation_flux=1.3),
Planet(planet_name='Wolf 1061 d', host_name='Wolf 1061', discovery_method='Radial Velocity', discovery_year=2015, controversial_flag=False, orbital_period=217.21, planet_radius=2.69, planet_mass=7.7, semi_major_radius=0.47, eccentricity=0.55, equilibrium_temperature=None, insolation_flux=0.06),
Planet(planet_name='YZ Cet b', host_name='YZ Cet', discovery_method='Radial Velocity', discovery_year=2017, controversial_flag=False, orbital_period=2.02087, planet_radius=0.913, planet_mass=0.7, semi_major_radius=0.01634, eccentricity=0.06, equilibrium_temperature=471.0, insolation_flux=8.21),
Planet(planet_name='YZ Cet c', host_name='YZ Cet', discovery_method='Radial Velocity', discovery_year=2017, controversial_flag=False, orbital_period=3.05989, planet_radius=1.05, planet_mass=1.14, semi_major_radius=0.02156, eccentricity=0.0, equilibrium_temperature=410.0, insolation_flux=4.72),
Planet(planet_name='YZ Cet d', host_name='YZ Cet', discovery_method='Radial Velocity', discovery_year=2017, controversial_flag=False, orbital_period=4.65626, planet_radius=1.03, planet_mass=1.09, semi_major_radius=0.02851, eccentricity=0.07, equilibrium_temperature=357.0, insolation_flux=2.7)]),
"16": (TEXT_FORMAT_ORDERED_LIST, [Planet(planet_name='Kepler-19 c', host_name='Kepler-19', discovery_method='Transit Timing Variations', discovery_year=2011, controversial_flag=False, orbital_period=28.731, planet_radius=3.68, planet_mass=13.1, semi_major_radius=None, eccentricity=0.21, equilibrium_temperature=None, insolation_flux=None),
Planet(planet_name='Kepler-19 d', host_name='Kepler-19', discovery_method='Radial Velocity', discovery_year=2017, controversial_flag=False, orbital_period=62.95, planet_radius=5.06, planet_mass=22.5, semi_major_radius=None, eccentricity=0.05, equilibrium_temperature=None, insolation_flux=None),
Planet(planet_name='Kepler-191 b', host_name='Kepler-191', discovery_method='Transit', discovery_year=2014, controversial_flag=False, orbital_period=9.939632, planet_radius=1.34, planet_mass=2.36, semi_major_radius=0.087, eccentricity=0.0, equilibrium_temperature=722.0, insolation_flux=64.29),
Planet(planet_name='Kepler-191 c', host_name='Kepler-191', discovery_method='Transit', discovery_year=2014, controversial_flag=False, orbital_period=17.738506, planet_radius=1.86, planet_mass=4.12, semi_major_radius=0.128, eccentricity=0.0, equilibrium_temperature=596.0, insolation_flux=29.74),
Planet(planet_name='Kepler-191 d', host_name='Kepler-191', discovery_method='Transit', discovery_year=2016, controversial_flag=False, orbital_period=5.94504102, planet_radius=2.28, planet_mass=5.82, semi_major_radius=0.0599, eccentricity=0.0, equilibrium_temperature=857.0, insolation_flux=127.64)]),
"17": (TEXT_FORMAT, 325),
"18": (TEXT_FORMAT_NAMEDTUPLE, Star(spectral_type='K8 V', stellar_effective_temperature=5144.0,
stellar_radius=0.79, stellar_mass=0.82, stellar_luminosity=-0.401,
stellar_surface_gravity=4.55, stellar_age=7.48)),
"19": (TEXT_FORMAT, 12.87916666666667),
"20": (TEXT_FORMAT_ORDERED_LIST, [Planet(planet_name='Kepler-1663 b', host_name='Kepler-1663',
discovery_method='Transit', discovery_year=2020,
controversial_flag=False, orbital_period=17.6046, planet_radius=3.304,
planet_mass=10.9, semi_major_radius=0.1072, eccentricity=0.0,
equilibrium_temperature=362.0, insolation_flux=4.07)])}
return expected_json
def check_cell(qnum, actual):
expected_json = return_expected_json()
format, expected = expected_json[qnum[1:]]
try:
if format == TEXT_FORMAT:
return simple_compare(expected, actual)
elif format == TEXT_FORMAT_UNORDERED_LIST:
return list_compare_unordered(expected, actual)
elif format == TEXT_FORMAT_ORDERED_LIST:
return list_compare_ordered(expected, actual)
elif format == TEXT_FORMAT_DICT:
return dict_compare(expected, actual)
elif format == TEXT_FORMAT_NAMEDTUPLE:
return namedtuple_compare(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 simple_compare(expected, actual, complete_msg=True):
msg = PASS
if type(expected) == type:
if expected != actual:
if type(actual) == type:
msg = "expected %s but found %s" % (expected.__name__, actual.__name__)
else:
msg = "expected %s but found %s" % (expected.__name__, repr(actual))
elif type(expected) != type(actual) and not (type(expected) in [float, int] and type(actual) in [float, int]):
msg = "expected to find type %s but found type %s" % (type(expected).__name__, type(actual).__name__)
elif type(expected) == float:
if not math.isclose(actual, expected, rel_tol=REL_TOL, abs_tol=ABS_TOL):
msg = "expected %s" % (repr(expected))
if complete_msg:
msg = msg + " but found %s" % (repr(actual))
else:
if expected != actual:
msg = "expected %s" % (repr(expected))
if complete_msg:
msg = msg + " but found %s" % (repr(actual))
return msg
namedtuples = ['Star', 'Planet']
star_attributes = ['spectral_type',
'stellar_effective_temperature',
'stellar_radius',
'stellar_mass',
'stellar_luminosity',
'stellar_surface_gravity',
'stellar_age']
# Create a namedtuple type, Star
Star = namedtuple("Star", star_attributes)
planets_attributes = ['planet_name',
'host_name',
'discovery_method',
'discovery_year',
'controversial_flag',
'orbital_period',
'planet_radius',
'planet_mass',
'semi_major_radius',
'eccentricity',
'equilibrium_temperature',
'insolation_flux']
# Create a namedtuple type, Planet
Planet = namedtuple("Planet", planets_attributes)
def namedtuple_compare(expected, actual):
msg = PASS
try:
actual_fields = actual._fields
except AttributeError:
msg = "expected namedtuple but found %s" % (type(actual).__name__)
return msg
if type(expected).__name__ != type(actual).__name__:
msg = "expected namedtuple %s but found namedtuple %s" % (type(expected).__name__, type(actual).__name__)
return msg
expected_fields = expected._fields
msg = list_compare_ordered(list(expected_fields), list(actual_fields), "namedtuple attributes")
if msg != PASS:
return msg
for field in expected_fields:
val = simple_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 list_compare_ordered(expected, actual, obj="list"):
msg = PASS
if type(expected) != type(actual):
msg = "expected to find type %s but found type %s" % (type(expected).__name__, type(actual).__name__)
return msg
for i in range(len(expected)):
if i >= len(actual):
msg = "expected missing %s in %s" % (repr(expected[i]), obj)
break
if type(expected[i]) in [int, float, bool, str]:
val = simple_compare(expected[i], actual[i])
elif type(expected[i]) in [list]:
val = list_compare_ordered(expected[i], actual[i], "sub" + obj)
elif type(expected[i]) in [dict]:
val = dict_compare(expected[i], actual[i])
elif type(expected[i]).__name__ in namedtuples:
val = namedtuple_compare(expected[i], actual[i])
if val != PASS:
msg = "at index %d of the %s, " % (i, obj) + val
break
if len(actual) > len(expected) and msg == PASS:
msg = "found unexpected %s in %s" % (repr(actual[len(expected)]), obj)
if len(expected) != len(actual):
msg = msg + " (found %d entries in %s, but expected %d)" % (len(actual), obj, len(expected))
if len(expected) > 0 and type(expected[0]) in [int, float, bool, str]:
if msg != PASS and list_compare_unordered(expected, actual, obj) == PASS:
try:
msg = msg + " (%s may not be ordered as required)" % (obj)
except:
pass
return msg
def list_compare_helper(larger, smaller):
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], False)
break
val = simple_compare(larger[i], smaller[j], False)
j += 1
if val == PASS:
found = True
break
if not found:
msg = val
break
return msg
def list_compare_unordered(expected, actual, obj="list"):
msg = PASS
if type(expected) != type(actual):
msg = "expected to find type %s but found type %s" % (type(expected).__name__, type(actual).__name__)
return msg
try:
sort_expected = sorted(expected)
sort_actual = sorted(actual)
except:
msg = "unexpected datatype found in %s; expected entries of type %s" % (obj, obj, type(expected[0]).__name__)
return msg
if len(actual) == 0 and len(expected) > 0:
msg = "in the %s, missing" % (obj) + expected[0]
elif len(actual) > 0 and len(expected) > 0:
val = simple_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 list_compare_special_init(expected, special_order):
real_expected = []
for i in range(len(expected)):
if real_expected == [] or special_order[i-1] != special_order[i]:
real_expected.append([])
real_expected[-1].append(expected[i])
return real_expected
def list_compare_special(expected, actual, special_order):
expected = list_compare_special_init(expected, special_order)
msg = PASS
expected_list = []
for expected_item in expected:
expected_list.extend(expected_item)
val = list_compare_unordered(expected_list, actual)
if val != PASS:
msg = val
else:
i = 0
for expected_item in expected:
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="dict"):
msg = PASS
if type(expected) != type(actual):
msg = "expected to find type %s but found type %s" % (type(expected).__name__, type(actual).__name__)
return msg
try:
expected_keys = sorted(list(expected.keys()))
actual_keys = sorted(list(actual.keys()))
except:
msg = "unexpected datatype found in keys of dict; expect a dict with keys of type %s" % (
type(expected_keys[0]).__name__)
return msg
val = list_compare_unordered(expected_keys, actual_keys, "dict")
if val != PASS:
msg = "bad keys in %s: " % (obj) + val
if msg == PASS:
for key in expected:
if expected[key] == None or type(expected[key]) in [int, float, bool, str]:
val = simple_compare(expected[key], actual[key])
elif type(expected[key]) in [list]:
val = list_compare_ordered(expected[key], actual[key], "value")
elif type(expected[key]) in [dict]:
val = dict_compare(expected[key], actual[key], "sub" + obj)
elif type(expected[key]).__name__ in namedtuples:
val = namedtuple_compare(expected[key], actual[key])
if val != PASS:
msg = "incorrect val for key %s in %s: " % (repr(key), obj) + val
return msg
def check(qnum, actual):
msg = check_cell(qnum, actual)
if msg == PASS:
return True
print("<b style='color: red;'>ERROR:</b> " + msg)
def check_file_size(path):
size = os.path.getsize(path)
assert size < MAX_FILE_SIZE * 10**3, "Your file is too big to be processed by Gradescope; please delete unnecessary output cells so your file size is < %s KB" % MAX_FILE_SIZE