diff --git a/sum23/labs/lab10/README.md b/sum23/labs/lab10/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..17b4a9e845486d15e1e4967c33923f4d786d1586
--- /dev/null
+++ b/sum23/labs/lab10/README.md
@@ -0,0 +1,61 @@
+# Lab 10: Files and Namedtuples
+
+In this lab, you'll get practice with files and namedtuples, in preparation for P10.
+
+-----------------------------
+## Corrections/Clarifications
+
+
+**Find any issues?** Please let Jane know during lab or create a Piazza post.
+
+------------------------------
+## Learning Objectives
+
+In this lab, you will practice...
+* Loading data in json files
+* Loading data in csv files
+* Using try/except to handle malformed data.
+
+------------------------------
+
+## Note on Academic Misconduct
+
+You may do these lab exercises only with your project partner; you are not allowed to start 
+working on Lab 10 with one person, then do the project with a different partner. Now may be a 
+good time to review [our course policies](https://canvas.wisc.edu/courses/355767/pages/syllabus?module_item_id=6048035).
+
+**Important:** P10 and P11 are two parts of the same data analysis.
+You **cannot** switch project partners between these two projects.
+If you partner up with someone for P10, you have to sustain that partnership until end of P11.
+
+------------------------------
+
+## Segment 1: Setup
+
+Create a `lab10` directory and download the following files into the `lab10` directory.
+
+* `small_data.zip`
+* `practice.ipynb`
+* `practice_test.py`
+
+After downloading `small_data.zip`, make sure to extract it (using [Mac directions](http://osxdaily.com/2017/11/05/how-open-zip-file-mac/) or [Windows directions](https://support.microsoft.com/en-us/help/4028088/windows-zip-and-unzip-files)). After extracting, you should see a folder called `small_data`, which has the following files in it:
+
+*` mapping_1.json`
+* `mapping_2.json`
+* `mapping_3.json`
+* `stars_1.csv`
+* `stars_2.csv`
+* `stars_3.csv`
+* `planets_1.csv`
+* `planets_2.csv`
+* `planets_3.csv`
+
+You may delete `small_data.zip` after extracting these files from it.
+
+
+## Segment 2: 
+For the remaining segments, detailed instructions are provided in `practice.ipynb`. From the terminal, open a `jupyter notebook` session, open your `practice.ipynb`, and follow the instructions in `practice.ipynb`.
+
+## Project 10
+
+You can now get started with [P10](https://git.doit.wisc.edu/cdis/cs/courses/cs220/cs220-lecture-material/-/tree/main/sum23/projects/p10). **You may copy/paste any code created here in project P10**. Have fun!
diff --git a/sum23/labs/lab10/images/files.jpg b/sum23/labs/lab10/images/files.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..fe0f2eee1a4c3c9e4ecf0860a14670ef122deb35
Binary files /dev/null and b/sum23/labs/lab10/images/files.jpg differ
diff --git a/sum23/labs/lab10/practice.ipynb b/sum23/labs/lab10/practice.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..fcec899f2cc6e42c3dac42e77092dad1e40a2f1c
--- /dev/null
+++ b/sum23/labs/lab10/practice.ipynb
@@ -0,0 +1,3009 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "7cb1e571",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "# Initialize Otter\n",
+    "import otter\n",
+    "grader = otter.Notebook(\"practice.ipynb\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "0c49b2a7",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import practice_test"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "808c1ea8",
+   "metadata": {},
+   "source": [
+    "# Lab 10:  File Handling and Namedtuples"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ee613db0",
+   "metadata": {},
+   "source": [
+    "## Learning Objectives:\n",
+    "\n",
+    "In this lab, you will practice how to...\n",
+    "* use the `os` module to handle files,\n",
+    "* load data in json files,\n",
+    "* combine data from different files to create data structures,\n",
+    "* create named tuples,\n",
+    "* use `try/except` to handle malformed data."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "0b2e0fb4",
+   "metadata": {},
+   "source": [
+    "## Note on Academic Misconduct:\n",
+    "\n",
+    "**IMPORTANT**: P10 and P11 are two parts of the same data analysis. You **cannot** switch project partners between these two projects. That is if you partner up with someone for Lab 10 and P10, you have to work on Lab 11 and P11 with the **same partner**.\n",
+    "\n",
+    "You may do these lab exercises with only with your project partner; you are not allowed to start working on Lab 10 with one person, then do the project with a different partner.  Now may be a good time to review [our course policies](https://canvas.wisc.edu/courses/355767/pages/syllabus?module_item_id=6048035)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3979d34c",
+   "metadata": {},
+   "source": [
+    "## Setup:\n",
+    "\n",
+    "Before proceeding much further, download `small_data.zip` and extract it to a directory on your\n",
+    "computer (using [Mac directions](http://osxdaily.com/2017/11/05/how-open-zip-file-mac/) or\n",
+    "[Windows directions](https://support.microsoft.com/en-us/help/4028088/windows-zip-and-unzip-files)).\n",
+    "\n",
+    "You need to make sure that the project files are stored in the following structure:\n",
+    "\n",
+    "```\n",
+    "+-- practice.ipynb\n",
+    "+-- practice_test.py\n",
+    "+-- small_data\n",
+    "|   +-- .DS_Store\n",
+    "|   +-- .ipynb_checkpoints\n",
+    "|   +-- mapping_1.json\n",
+    "|   +-- mapping_2.json\n",
+    "|   +-- mapping_3.json\n",
+    "|   +-- planets_1.csv\n",
+    "|   +-- planets_2.csv\n",
+    "|   +-- planets_3.csv\n",
+    "|   +-- stars_1.csv\n",
+    "|   +-- stars_2.csv\n",
+    "|   +-- stars_3.csv\n",
+    "```\n",
+    "\n",
+    "Make sure that the files inside `small_data.zip` are inside the `small_data` directory."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3e29243c",
+   "metadata": {},
+   "source": [
+    "## Introduction:\n",
+    "\n",
+    "In P10 and P11, we will be studying stars and planets outside our Solar System using this dataset from the [NASA Exoplanet Archive](https://exoplanetarchive.ipac.caltech.edu/cgi-bin/TblView/nph-tblView?app=ExoTbls&config=PSCompPars). We will use Python to ask some interesting questions about the laws of the universe and explore the habitability of other planets in our universe.\n",
+    "\n",
+    "In Lab 10, you will work with a small subset of the full dataset. You can find these files inside `small_data.zip`. The full dataset used in P10 and P11 is stored in the same format, so you can then use this code to parse the dataset in P10 and P11."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "8089eb9e",
+   "metadata": {},
+   "source": [
+    "## The Data:\n",
+    "\n",
+    "You can open each of the files inside the `small_data` directory using Microsoft Excel or some other Spreadsheet viewing software to see how the data is stored. For example, these are the contents of the file `stars_1.csv`:\n",
+    "\n",
+    "|Name|Spectral Type|Stellar Effective Temperature [K]|Stellar Radius [Solar Radius]|Stellar Mass [Solar mass]|Stellar Luminosity [log(Solar)]|Stellar Surface Gravity [log10(cm/s**2)]|Stellar Age [Gyr]|\n",
+    "|----|-------------|---------------------------------|-----------------------------|-------------------------|-------------------------------|----------------------------------------|-----------------|\n",
+    "|55 Cnc|G8V|5172.00|0.94|0.91|-0.197|4.43|10.200|\n",
+    "|DMPP-1|F8 V|6196.00|1.26|1.21|0.320|4.41|2.010|\n",
+    "|GJ 876|M2.5V|3271.00|0.30|0.32|-1.907|4.87|1.000|\n",
+    "\n",
+    "As you might have already guessed, this file contains data on a number of *stars* outside our solar system along with some important statistics about these stars. The columns here are as follows:\n",
+    "\n",
+    "- `Name`: The name given to the star by the International Astronomical Union,\n",
+    "- `Spectral Type`: The Spectral Classification of the star as per the Morgan–Keenan (MK) system,\n",
+    "- `Stellar Effective Temperature [K]`: The temperature of a black body (in units of Kelvin) that would emit the observed radiation of the star,\n",
+    "- `Stellar Radius [Solar Radius]`: The radius of the star (in units of the radius of the Sun),\n",
+    "- `Stellar Mass [Solar mass]`: The mass of the star (in units of the mass of the Sun),\n",
+    "- `Stellar Luminosity [log(Solar)]`: The total amount of energy radiated by the star each second (represented by the logarithm of the energy radiated by the Sun in each second),\n",
+    "- `Stellar Surface Gravity [log10(cm/s**2)]`: The acceleration due to the gravity of the Star at its surface (represented by the logarithm of the acceleration measured in centimeter per second squared),\n",
+    "- `Stellar Age [Gyr]`: The total age of the star (in units of Giga years, i.e., billions of years).\n",
+    "\n",
+    "The two other files `stars_2.csv`, and `stars_3.csv` also store similar data in the same format. At this stage, it is alright if you do not understand what these columns mean - they will be explained to you when they become necessary (in P10 and P11).\n",
+    "\n",
+    "On the other hand, here are the contents of the file `planets_1.csv`:\n",
+    "\n",
+    "|Planet Name|Discovery Method|Discovery Year|Controversial Flag|Orbital Period [days]|Planet Radius [Earth Radius]|Planet Mass [Earth Mass]|Orbit Semi-Major Axis [au]|Eccentricity|Equilibrium Temperature [K]|Insolation Flux [Earth Flux]|\n",
+    "|-----------|----------------|--------------|------------------|---------------------|----------------------------|------------------------|---------------------------|------------|---------------------------|----------------------------|\n",
+    "|55 Cnc b|Radial Velocity|1996|0|14.65160000|13.900|263.97850|0.113400|0.000000|700||\n",
+    "|55 Cnc c|Radial Velocity|2004|0|44.39890000|8.510|54.47380|0.237300|0.030000|||\n",
+    "|DMPP-1 b|Radial Velocity|2019|0|18.57000000|5.290|24.27000|0.146200|0.083000|877||\n",
+    "|GJ 876 b|Radial Velocity|1998|0|61.11660000|13.300|723.22350|0.208317|0.032400|||\n",
+    "|GJ 876 c|Radial Velocity|2000|0|30.08810000|14.000|226.98460|0.129590|0.255910|||\n",
+    "\n",
+    "\n",
+    "This file contains data on a number of *planets* outside our solar system along with some important statistics about these planets. The columns here are as follows:\n",
+    "\n",
+    "- `Planet Name`: The name given to the planet by the International Astronomical Union,\n",
+    "- `Discovery Method`: The method by which the planet was discovered,\n",
+    "- `Discovery Year`: The year in which the planet was discovered,\n",
+    "- `Controversial Flag`: Indicates whether the status of the discovered object as a planet was disputed at the time of discovery, \n",
+    "- `Orbital Period [days]`: The amount of time (in units of days) it takes for the planet to complete one orbit around its star,\n",
+    "- `Planet Radius [Earth Radius]`: The radius of the planet (in units of the radius of the Earth),\n",
+    "- `Planet Mass [Earth Mass]`: The mass of the planet (in units of the mass of the Earth),\n",
+    "- `Orbit Semi-Major Axis [au]`: The semi-major axis of the planet's elliptical orbit around its host star (in units of Astronomical Units),\n",
+    "- `Eccentricity`: The eccentricity of the planet's orbit around its host star,\n",
+    "- `Equilibrium Temperature [K]`: The temperature of the planet (in units of Kelvin) if it were a black body heated only by its host star,\n",
+    "- `Insolation Flux [Earth Flux]`:  The amount of radiation the planet received from its host star per unit of area (in units of the Insolation Flux of the Earth from the Sun).\n",
+    "\n",
+    "The two other files `planets_2.csv`, and `planets_3.csv` also store similar data in the same format.\n",
+    "\n",
+    "\n",
+    "Finally, if you take a look at `mapping_1.json` (you can open json files using any Text Editor), you will see that the file looks like this:\n",
+    "\n",
+    "```\n",
+    "{\"55 Cnc b\": \"55 Cnc\", \"55 Cnc c\": \"55 Cnc\", \"DMPP-1 b\": \"DMPP-1\", \"GJ 876 b\": \"GJ 876\", \"GJ 876 c\": \"GJ 876\"}\n",
+    "```\n",
+    "\n",
+    "This file contains a *mapping* from each *planet* in `planets_1.csv` to the *star* in `stars_1.csv` that the planet orbits. Similarly, `mapping_2.json` contains a *mapping* from each *planet* in `planets_2.csv` to the *star* in `stars_2.csv` that the planet orbits, and `mapping_3.json` contains a *mapping* from each *planet* in `planets_3.csv` to the *star* in `stars_3.csv` that the planet orbits."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "315474e8",
+   "metadata": {},
+   "source": [
+    "## Questions and Functions:\n",
+    "\n",
+    "Let us start by importing all the modules we will need for this project."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "27fac496",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# it is considered a good coding practice to place all import statements at the top of the notebook\n",
+    "# place all your import statements in this cell if you need to import any more modules for this project\n",
+    "\n",
+    "# we have imported these modules for you\n",
+    "import os\n",
+    "from collections import namedtuple\n",
+    "import csv\n",
+    "import json"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "09dd2aa8",
+   "metadata": {},
+   "source": [
+    "## Segment 2: File handling with the `os` module\n",
+    "\n",
+    "In this segment, you will learn how to use the `os` module effectively."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d9ce1f1d",
+   "metadata": {},
+   "source": [
+    "**Question 1.1**: List **all** the files and directories in the directory `small_data` using the `os.listdir` function.\n",
+    "\n",
+    "Your output **must** be a **list** of **strings**. The order does **not** matter."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "37d1f005",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# we have done this one for you\n",
+    "\n",
+    "all_files = os.listdir('small_data')\n",
+    "\n",
+    "all_files"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "fdd54231",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q1-1\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "cd5fa410",
+   "metadata": {},
+   "source": [
+    "**Important Warning:** That appeared to work just fine, but you should be **very careful** when using the `os` module. You might have noticed that there are files and directories in the list returned by `os.listdir` that **begin** with the character `\".\"` (specifically in this case, the file `\".DS_Store\"` and the directory `\".ipynb_checkpoints\"`). Such files and directories are used by some operating systems to store metadata. These files are not actually a part of your dataset, and must be **ignored**. \n",
+    "\n",
+    "When you are processing the files in any directory, you **must** always **ignore** such files that begin with the character `\".\"`, as they are not actually files in the directory. You **must** do this every time you use `os.listdir`."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "76ff4730",
+   "metadata": {},
+   "source": [
+    "**Question 1.2**: List **all** the files and directories in the directory `small_data` that do **not** **start with** the character`\".\"`.\n",
+    "\n",
+    "Your output **must** be a **list** of **strings**. The order does **not** matter."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "6200029f",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'actual_files', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "81413fc8",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q1-2\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "0baf9066",
+   "metadata": {},
+   "source": [
+    "**Important Warning:** You are not done yet. Look at the order in which the files in the **list** `actual_files` are stored. The **ordering** of the files in the **list** returned by `os.listdir` **depends on the operating system**. This means that if you run this code on a **different OS**, the files might be sorted in a **different order**. This makes `os.listdir` a little dangerous because you could index it, and it will always work the same way on your computer, but will **behave differently on another computer**. To avoid these issues, you should make sure that you always **sort** the output of `os.listdir` before you use it. This will ensure that the ordering remains consistent across all operating systems.\n",
+    "\n",
+    "When you are processing the files in any directory, you **must** always **sort** the output of `os.listdir` first. You **must** do this every time you use `os.listdir`."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a7dee7c7",
+   "metadata": {},
+   "source": [
+    "**Question 2**: List **all** the files and directories in the directory `small_data` that do **not** **start with** the character`\".\"`, sorted in **reverse alphabetical order**.\n",
+    "\n",
+    "Your output **must** be a **list** of **strings**, sorted in **reverse alphabetical** order."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "8396572e",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'files_in_small_data', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "35670603",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q2\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e38c5599",
+   "metadata": {},
+   "source": [
+    "**Important Warning:** Every time you use `os.listdir`, you **must** **ignore** files and directories that start with `\".\"`, and also **sort** the **list** returned by the function, before you do anything else. Otherwise, you are likely to write code that **works on your computer**, but **crashes on other computers**. Such errors are hard to debug, and you **must** be very careful."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a1068d6e",
+   "metadata": {},
+   "source": [
+    "**Question 3.1**: What is the **path** of the file `stars_1.csv` in the directory `small_data`.\n",
+    "\n",
+    "You are **allowed** to 'hardcode' the strings `'small_data'` and `'stars_1.csv'` to answer this question.\n",
+    "\n",
+    "**Warnings:**\n",
+    "\n",
+    "1. You **must not** hardcode the **absolute path** of any file in your code. For instance, the **absolute path** of this file `stars_1.csv` could be: `C:\\Users\\ms\\cs220\\lab-p10\\small_data\\stars_1.csv`. However, if you hardcode this path in your code, it will **only work on your computer**. In this case, since the notebook `practice.ipynb` is stored in the path `C:\\Users\\ms\\cs220\\lab-p10`, the **relative path** of the file is `small_data\\stars_1.csv`, and this is the path that **must** be used, if you want your code to work on all computers.\n",
+    "2. You **must not** hardcode either the character `\"\\\"` or the character `\"/\"` in your paths. If you do so, your code will **crash** when it runs on a **different operating system**. You **must** use the `os.path.join` function to create paths."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "07c47fa4",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# we have done this one for you\n",
+    "\n",
+    "stars_1_path = os.path.join(\"small_data\", \"stars_1.csv\")\n",
+    "\n",
+    "stars_1_path"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f461a00b",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q3-1\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "8fc50972",
+   "metadata": {},
+   "source": [
+    "**Question 3.2**: List the **paths** of **all** the files in the directory `small_data`.\n",
+    "\n",
+    "Your output **must** be a **list** of **strings**. You must **ignore** files that **start with** the character`\".\"`, and your output **must** be sorted in **reverse alphabetical order**.\n",
+    "\n",
+    "You are **allowed** to \"hardcode\" the name of the directory `small_data` to answer this question.\n",
+    "\n",
+    "**Warnings:**\n",
+    "\n",
+    "1. You **must not** hardcode the **absolute path** of any file in your code. You must use the **relative path** of the files.\n",
+    "2. You **must not** hardcode either the character `\"\\\"` or the character `\"/\"` in your paths. You **must** use the `os.path.join` function to create paths."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "ba9aa18a",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'paths_in_small_data', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "d37f8b04",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q3-2\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "bc4582af",
+   "metadata": {},
+   "source": [
+    "**Question 4.1**: List the **paths** of **all** the JSON files in the directory `small_data`.\n",
+    "\n",
+    "Your output **must** be a **list** of **strings**. You must **ignore** files that **start with** the character`\".\"`, and your output **must** sorted in **reverse alphabetical order**.\n",
+    "\n",
+    "**Hint:** You can identify the JSON files as the files which end with the string `\".json\"`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "2078e3cb",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'json_paths', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "6050a736",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q4-1\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "15892c6d",
+   "metadata": {},
+   "source": [
+    "**Question 4.2**: List the **paths** of **all** the files in the directory `small_data`, whose filename starts with `\"stars\"`.\n",
+    "\n",
+    "Your output **must** be a **list** of **strings**. You must **ignore** files that **start with** the character`\".\"`, and your output **must** sorted in **reverse alphabetical order**."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "4219e195",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'stars_paths', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "225d3b31",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q4-2\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "944a550e",
+   "metadata": {},
+   "source": [
+    "## Segment 3: Creating Namedtuples\n",
+    "\n",
+    "In P10, you will be reading the data in files similar to `stars_1.csv`, `stars_2.csv`, and `stars_3.csv`, and storing the data as a **dictionary** of **named tuples**. Now would be a great time to practice creating similar data structues."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c2359d2d",
+   "metadata": {},
+   "source": [
+    "### Data Structure 1: namedtuple `Star`\n",
+    "\n",
+    "We will now create a new `Star` type (using namedtuple). It **must** have the following attributes:\n",
+    "\n",
+    "* `spectral_type`,\n",
+    "* `stellar_effective_temperature`,\n",
+    "* `stellar_radius`,\n",
+    "* `stellar_mass`,\n",
+    "* `stellar_luminosity`,\n",
+    "* `stellar_surface_gravity`,\n",
+    "* `stellar_age`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "fa14b675",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# we have done this one for you\n",
+    "\n",
+    "# define the list of attributes we want in our namedtuple\n",
+    "star_attributes = ['spectral_type',\n",
+    "                  'stellar_effective_temperature',\n",
+    "                  'stellar_radius',\n",
+    "                  'stellar_mass',\n",
+    "                  'stellar_luminosity',\n",
+    "                  'stellar_surface_gravity',\n",
+    "                  'stellar_age']\n",
+    "\n",
+    "# create the namedtuple type 'Star' with the correct attributes\n",
+    "Star = namedtuple(\"Star\", star_attributes)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "645280cf",
+   "metadata": {},
+   "source": [
+    "Let us now test whether we have defined the namedtuple properly by creating a `Star` object."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "fa8f9792",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# run this following cell to initialize and test an example Star object\n",
+    "\n",
+    "sun = Star('G2 V', 5780.0, 1.0, 1.0, 0.0, 4.44, 4.6)\n",
+    "\n",
+    "sun"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b1211c9d",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"star_object\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "effba07e",
+   "metadata": {},
+   "source": [
+    "### Segment 3.1: Creating `Star` objects from `stars_1.csv`\n",
+    "\n",
+    "Now that we have created the `Star` namedtuple, our next objective will be to read the files `stars_1.csv`, `stars_2.csv`, and `stars_3.csv` and create `Star` objects out of all the stars in there. In order to process the CSV files, you will first need to copy/paste the `process_csv` function you have been using since P6."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "70577d4f",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# copy & paste the process_csv file from previous projects here\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "af903947",
+   "metadata": {},
+   "source": [
+    "You are now ready to read the data in `stars_1.csv` using `process_csv` and convert the data into `Star` objects. In the cell below, you **must** read the data in `stars_1.csv` and extract the **header** and the non-header **rows** of the file."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "dc9d27a4",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# replace the ... with your code\n",
+    "\n",
+    "stars_1_csv = process_csv(os.path.join(\"small_data\", \"stars_1.csv\")) # read the data in 'stars_1.csv'\n",
+    "stars_header = ...\n",
+    "stars_1_rows = ..."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "770e991c",
+   "metadata": {},
+   "source": [
+    "If you wish to **verify** that you have read the file and defined the variables correctly, you can check that `stars_header` has the value:\n",
+    "\n",
+    "```python\n",
+    "['Name', 'Spectral Type', 'Stellar Effective Temperature [K]', 'Stellar Radius [Solar Radius]', \n",
+    " 'Stellar Mass [Solar mass]', 'Stellar Luminosity [log(Solar)]', 'Stellar Surface Gravity [log10(cm/s**2)]',\n",
+    " 'Stellar Age [Gyr]']\n",
+    "```\n",
+    "\n",
+    "and that `stars_1_rows` has the value:\n",
+    "\n",
+    "```python\n",
+    "[['55 Cnc', 'G8V', '5172.00', '0.94', '0.91', '-0.197', '4.43', '10.200'],\n",
+    " ['DMPP-1', 'F8 V', '6196.00', '1.26', '1.21', '0.320', '4.41', '2.010'],\n",
+    " ['GJ 876', 'M2.5V', '3271.00', '0.30', '0.32', '-1.907', '4.87', '1.000']]\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "59589559",
+   "metadata": {},
+   "source": [
+    "**Question 5**: Create a `Star` object for the **first** star in `\"stars_1.csv\"`.\n",
+    "\n",
+    "The **attribute** of the `Star` namedtuple object, the corresponding **column** of the `stars_1.csv` file where the value should be obtained from, and the correct **data type** for the value are listed in the table below:\n",
+    "\n",
+    "|Attribute of `Star` object|Column of `stars_1.csv`|Data Type|\n",
+    "|---------|------|---------|\n",
+    "|`spectral_type`|Spectral Type|**string**|\n",
+    "|`stellar_effective_temperature`|Stellar Effective Temperature [K]|**float**|\n",
+    "|`stellar_radius`|Stellar Radius [Solar Radius]|**float**|\n",
+    "|`stellar_mass`|Stellar Mass [Solar mass]|**float**|\n",
+    "|`stellar_luminosity`|Stellar Luminosity [log(Solar)]|**float**|\n",
+    "|`stellar_surface_gravity`|Stellar Surface Gravity [log10(cm/s**2)]|**float**|\n",
+    "|`stellar_age`|Stellar Age [Gyr]|**float**|"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "809ef473",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# replace the ... with your code\n",
+    "\n",
+    "row_idx = 0 # the index of the star we want to convert into a Star object\n",
+    "\n",
+    "# extract the values from stars_1_rows\n",
+    "spectral_type = stars_1_rows[row_idx][stars_header.index(...)]\n",
+    "stellar_effective_temperature = float(stars_1_rows[row_idx][stars_header.index(...)])\n",
+    "stellar_radius = ...\n",
+    "stellar_mass = ...\n",
+    "stellar_luminosity = ...\n",
+    "stellar_surface_gravity = ...\n",
+    "stellar_age = ...\n",
+    "\n",
+    "# initialize 'first_star'\n",
+    "first_star = Star(spectral_type, stellar_effective_temperature, stellar_radius, \\\n",
+    "                  stellar_mass, stellar_luminosity, \\\n",
+    "                  stellar_surface_gravity, stellar_age)\n",
+    "\n",
+    "first_star"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "47721e36",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q5\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "8ec9a172",
+   "metadata": {},
+   "source": [
+    "**Question 6**: Create a `Star` object for the **second** star in `\"stars_1.csv\"`.\n",
+    "\n",
+    "You **must** create the `Star` object similarly to what you did in the previous question."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "57de668f",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'second_star', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "53ac49fc",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q6\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d2934799",
+   "metadata": {},
+   "source": [
+    "**Question 7.1**: What is the `spectral_type` of the **second** star in `\"stars_1.csv\"`?\n",
+    "\n",
+    "You **must** answer this question by accessing the correct **attribute** of the `Star` object `second_star`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "16028988",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# we have done this one for you\n",
+    "\n",
+    "second_star_spectral_type = second_star.spectral_type\n",
+    "\n",
+    "second_star_spectral_type"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "0f60ee11",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q7-1\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "22ff4de6",
+   "metadata": {},
+   "source": [
+    "**Question 7.2**: What is the `stellar_age` of the **first** star in `\"stars_1.csv\"`?\n",
+    "\n",
+    "You **must** answer this question by accessing the correct **attribute** of the `Star` object `first_star`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "d237a1e0",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'first_star_stellar_age', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "98c3555f",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q7-2\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "cbb5dc03",
+   "metadata": {},
+   "source": [
+    "**Question 7.3**: What is the **ratio** of the `stellar_radius` of the **first** star in `\"stars_1.csv\"` to the **second** star in `\"stars_1.csv\"`?\n",
+    "\n",
+    "You **must** answer this question by accessing the correct **attribute** of the `Star` objects `first_star` and `second_star`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "401b5887",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'stellar_radius_ratio', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b40d09ff",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q7-3\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3cc4eb5b",
+   "metadata": {},
+   "source": [
+    "**Question 8**: Create a **dictionary** mapping the `name` of each star in `\"stars_1.csv\"` to its `Star` object.\n",
+    "\n",
+    "Your output **must** look like this:\n",
+    "```python\n",
+    "{'55 Cnc': Star(spectral_type='G8V', stellar_effective_temperature=5172.0, stellar_radius=0.94, \n",
+    "                stellar_mass=0.91, stellar_luminosity=-0.197, stellar_surface_gravity=4.43, stellar_age=10.2),\n",
+    " 'DMPP-1': Star(spectral_type='F8 V', stellar_effective_temperature=6196.0, stellar_radius=1.26, \n",
+    "                stellar_mass=1.21, stellar_luminosity=0.32, stellar_surface_gravity=4.41, stellar_age=2.01),\n",
+    " 'GJ 876': Star(spectral_type='M2.5V', stellar_effective_temperature=3271.0, stellar_radius=0.3, \n",
+    "                stellar_mass=0.32, stellar_luminosity=-1.907, stellar_surface_gravity=4.87, stellar_age=1.0)}\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "502c6cca",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# replace the ... with your code\n",
+    "\n",
+    "stars_1_dict = {} # initialize empty dictionary to store all stars\n",
+    "\n",
+    "for row_idx in range(len(stars_1_rows)):\n",
+    "    star_name = stars_1_rows[row_idx][stars_header.index(...)]\n",
+    "    spectral_type = ...\n",
+    "    stellar_effective_temperature = ...\n",
+    "    # extract the other columns from 'stars_1_rows'\n",
+    "    \n",
+    "    star = ... # initialize the 'Star' object using the variables defined above\n",
+    "    stars_1_dict[...] = star\n",
+    "\n",
+    "stars_1_dict"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "fd6e88ce",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "stars_1_rows"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "816ac420",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q8\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "11f221a4",
+   "metadata": {},
+   "source": [
+    "**Question 9.1**: What is the `Star` object of the star (in `stars_1.csv`) named *GJ 876*?\n",
+    "\n",
+    "You **must** access the `Star` object in `stars_1_dict` **dictionary** defined above to answer this question."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "5103a392",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'gj_876', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9db215ed",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q9-1\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "86456ed6",
+   "metadata": {},
+   "source": [
+    "**Question 9.2**: What is the `stellar_luminosity` of the star (in `stars_1.csv`) named *GJ 876*?\n",
+    "\n",
+    "You **must** access the `Star` object in `stars_1_dict` **dictionary** defined above to answer this question."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "fa69195b",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'gj_876_luminosity', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "d22185ca",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q9-2\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a24daea4",
+   "metadata": {},
+   "source": [
+    "### Segment 3.2: Data Cleaning - missing data\n",
+    "\n",
+    "We have already parsed the data in `stars_1.csv`. We are now ready to parse the data in **all** the star files of the `small_data` directory. However, there is one minor inconvenience - there is some missing data in `stars_2.csv` and `stars_3.csv`. For example, this is the **first** row of `stars_2.csv`:\n",
+    "\n",
+    "```python\n",
+    "['HD 158259', 'G0', '5801.89', '1.21', '1.08', '0.212', '4.25', '']\n",
+    "```\n",
+    "\n",
+    "As you can see, the value of the last column (`Stellar Age [Gyr]`) is `''`, which means that the data is missing. When the data is missing, we will want the value of the corresponding attribute in the `Star` object to be `None`.\n",
+    "\n",
+    "So, for example, if we are to convert the row above to be a `Star` object, it should look like:\n",
+    "\n",
+    "```python\n",
+    "Star(spectral_type='G0', stellar_effective_temperature=5801.89, stellar_radius=1.21, stellar_mass=1.08,\n",
+    "     stellar_luminosity=0.212, stellar_surface_gravity=4.25, stellar_age=None)\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "16f13f76",
+   "metadata": {},
+   "source": [
+    "### Function 1: `star_cell(row_idx, col_name, stars_rows, header=stars_header)`\n",
+    "\n",
+    "Since we need to clean the values of the **list** of **lists** `stars_rows` before we can create our required data structure (**dictionary** mapping **strings** to `Star` objects), now would be a good time to create a function that takes in a `row_idx`, a `col_name` and a **list** of **lists** `stars_rows` (as well as the optional argument `header`) and returns the value of the column `col_name` at the row `row_idx`.\n",
+    "\n",
+    "This function **must** typecast the values it returns based on the `col_name`. If the value in `stars_rows` is missing (i.e., it is `''`), then the value returned **must** be `None`.\n",
+    "\n",
+    "Recall that the **column** of `stars_rows` where the value should be obtained from, and the correct **data type** for the value are listed in the table below:\n",
+    "\n",
+    "|Column of `stars_rows`|Data Type|\n",
+    "|------|---------|\n",
+    "|Name|**string**|\n",
+    "|Spectral Type|**string**|\n",
+    "|Stellar Effective Temperature [K]|**float**|\n",
+    "|Stellar Radius [Solar Radius]|**float**|\n",
+    "|Stellar Mass [Solar mass]|**float**|\n",
+    "|Stellar Luminosity [log(Solar)]|**float**|\n",
+    "|Stellar Surface Gravity [log10(cm/s**2)]|**float**|\n",
+    "|Stellar Age [Gyr]|**float**|\n",
+    "\n",
+    "**Hint:** You can use the `cell` function defined in P6 and P7 for inspiration here."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f89e2a16",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# replace the ... with your code\n",
+    "\n",
+    "# the default argument to the parameter 'header' is the global variable 'stars_header' defined above\n",
+    "def star_cell(row_idx, col_name, stars_rows, header=stars_header):\n",
+    "    col_idx = header.index(...)\n",
+    "    val = stars_rows[row_idx][col_idx]\n",
+    "    # return None if value is missing\n",
+    "    # else typecast 'val' and return it depending on 'col_name'"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "85c16a43",
+   "metadata": {},
+   "source": [
+    "**Question 10.1**: Use the `star_cell` function to find the value of the column `\"Spectral Type\"` of the **first** star in `\"stars_2.csv\"`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "4ad3d789",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# we have done this one for you\n",
+    "\n",
+    "# first read the data in 'stars_2.csv' as a list of lists\n",
+    "stars_2_data = process_csv(os.path.join(\"small_data\", \"stars_2.csv\"))\n",
+    "stars_2_rows = stars_2_data[1:]\n",
+    "\n",
+    "# use the 'star_cell' function to extract the correct value\n",
+    "first_star_type = star_cell(0, 'Spectral Type', stars_2_rows)\n",
+    "\n",
+    "first_star_type"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e832900c",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q10-1\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "58ebba21",
+   "metadata": {},
+   "source": [
+    "**Question 10.2**: Use the `star_cell` function to find the value of the column `\"Stellar Age [Gyr]\"` of the **second** star in `\"stars_2.csv\"`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9c5b7bd3",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# we have done this one for you\n",
+    "# do not worry if there is no output, the variable is expected to hold the value None\n",
+    "\n",
+    "# use the 'star_cell' function to extract the correct value\n",
+    "second_star_age = star_cell(1, 'Stellar Age [Gyr]', stars_2_rows)\n",
+    "\n",
+    "second_star_age"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "66c21d45",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q10-2\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "73b6f8f3",
+   "metadata": {},
+   "source": [
+    "**Question 10.3**: Use the `star_cell` function to find the value of the column `\"Stellar Mass [Solar mass]\"` of the **third** star in `\"stars_2.csv\"`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "d6e075ce",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# we have done this one for you\n",
+    "\n",
+    "# use the 'star_cell' function to extract the correct value\n",
+    "third_star_mass = star_cell(2, 'Stellar Mass [Solar mass]', stars_2_rows)\n",
+    "\n",
+    "third_star_mass"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "855ea6c0",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q10-3\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3028047c",
+   "metadata": {},
+   "source": [
+    "**Question 11**: Create a **dictionary** mapping the `name` of each star in `\"stars_2.csv\"` to its `Star` object.\n",
+    "\n",
+    "You **must** use the `star_cell` function to extract data from `stars_2.csv`.\n",
+    "\n",
+    "Your output **must** look like this:\n",
+    "```python\n",
+    "{'HD 158259': Star(spectral_type='G0', stellar_effective_temperature=5801.89, stellar_radius=1.21, \n",
+    "                   stellar_mass=1.08, stellar_luminosity=0.212, stellar_surface_gravity=4.25, stellar_age=None),\n",
+    " 'K2-187': Star(spectral_type=None, stellar_effective_temperature=5438.0, stellar_radius=0.83, \n",
+    "                stellar_mass=0.97, stellar_luminosity=-0.21, stellar_surface_gravity=4.6, stellar_age=None),\n",
+    " 'WASP-47': Star(spectral_type=None, stellar_effective_temperature=5552.0, stellar_radius=1.14, \n",
+    "                 stellar_mass=1.04, stellar_luminosity=0.032, stellar_surface_gravity=4.34, stellar_age=6.5)}\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f6256a35",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# replace the ... with your code\n",
+    "\n",
+    "stars_2_dict = {} # initialize empty dictionary to store all stars\n",
+    "\n",
+    "for row_idx in range(len(stars_2_rows)):\n",
+    "    star_name = star_cell(row_idx, 'Name', stars_2_rows)\n",
+    "    spectral_type = ...\n",
+    "    stellar_effective_temperature = ...\n",
+    "    # extract the other columns from 'stars_2_rows'\n",
+    "    \n",
+    "    star = ... # initialize the 'Star' object using the variables defined above\n",
+    "    stars_2_dict[...] = star\n",
+    "\n",
+    "stars_2_dict"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "751fe1be",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q11\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "55712f32",
+   "metadata": {},
+   "source": [
+    "**Question 12.1**: Create a **dictionary** mapping the `name` of each star in `\"stars_3.csv\"` to its `Star` object.\n",
+    "\n",
+    "You **must** use the `star_cell` function to extract data from `stars_3.csv`.\n",
+    "\n",
+    "Your output **must** look like this:\n",
+    "```python\n",
+    "{'K2-133': Star(spectral_type='M1.5 V', stellar_effective_temperature=3655.0, stellar_radius=0.46, \n",
+    "                stellar_mass=0.46, stellar_luminosity=-1.479, stellar_surface_gravity=4.77, stellar_age=None),\n",
+    " 'K2-138': Star(spectral_type='G8 V', stellar_effective_temperature=5356.3, stellar_radius=0.86, \n",
+    "                stellar_mass=0.94, stellar_luminosity=-0.287, stellar_surface_gravity=4.54, stellar_age=2.8),\n",
+    " 'GJ 667 C': Star(spectral_type='M1.5 V', stellar_effective_temperature=3350.0, stellar_radius=None, \n",
+    "                  stellar_mass=0.33, stellar_luminosity=-1.863, stellar_surface_gravity=4.69, stellar_age=2.0)}\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "adb9098c",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'stars_3_dict', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e4bc9239",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q12-1\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "bcd64cc9",
+   "metadata": {},
+   "source": [
+    "**Question 12.2**: Combine the three **dictionaries** `stars_1_dict`, `stars_2_dict`, and `stars_3_dict` into a single **dictionary** with all the stars in the `small_data` directory."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "083bf8d1",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# replace the ... with your code\n",
+    "\n",
+    "stars_dict = ... # initialize an empty dictionary\n",
+    "stars_dict.update(...) # add stars_1_dict to stars_dict\n",
+    "# add stars_2_dict and stars_3_dict to stars_dict\n",
+    "\n",
+    "stars_dict"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "c05baf33",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q12-2\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3830125c",
+   "metadata": {},
+   "source": [
+    "### Data Structure 2: namedtuple `Planet`\n",
+    "\n",
+    "Just as you did with the stars, you will be using named tuples to store the data about the planets in the `planets_1.csv`, `planets_2.csv`, and `planets_3.csv` files. Before you start reading these files however, you **must** create a new `Planet` type (using namedtuple). It **must** have the following attributes:\n",
+    "\n",
+    "* `planet_name`,\n",
+    "* `host_name`,\n",
+    "* `discovery_method`,\n",
+    "* `discovery_year`,\n",
+    "* `controversial_flag`,\n",
+    "* `orbital_period`,\n",
+    "* `planet_radius`,\n",
+    "* `planet_mass`,\n",
+    "* `semi_major_radius`,\n",
+    "* `eccentricity`,\n",
+    "* `equilibrium_temperature`\n",
+    "* `insolation_flux`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "c91d9990",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# define the namedtuple 'Planet' here\n",
+    "\n",
+    "planets_attributes = ... # initialize the list of attributes\n",
+    "\n",
+    "# define the namedtuple 'Planet'\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "12727d44",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# run this following cell to initialize and test an example Planet object\n",
+    "# if this cell fails to execute, you have likely not defined the namedtuple 'Planet' correctly\n",
+    "jupiter = Planet('Jupiter', 'Sun', 'Imaging', 1610, False, 4333.0, 11.209, 317.828, 5.2038, 0.0489, 110, 0.0345)\n",
+    "\n",
+    "jupiter"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "a9d90d36",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"planet_object\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "b7e7130d",
+   "metadata": {},
+   "source": [
+    "### Segment 3.3: Creating `Planet` objects\n",
+    "\n",
+    "We are now ready to read the files in the `small_data` directory and create `Planet` objects. Creating `Planet` objects however, is going to be more difficult than creating `Star` objects, because the data required to create a single `Planet` object is split up into different files.\n",
+    "\n",
+    "The `planets_1.csv`, `planets_2.csv`, and `planets_3.csv` files contain all the data required to create `Planet` objects **except** for the `host_name`. The `host_name` for each planet is to be found in the `mapping_1.json`, `mapping_2.json`, and `mapping_3.json` files."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a0c09672",
+   "metadata": {},
+   "source": [
+    "First, let us read the data in `planets_1.csv`. Since this is a CSV file, you can use the `process_csv` function from above to read this file. In the cell below, you **must** read the data in `planets_1.csv` and extract the **header** and the non-header **rows** of the file."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "dbd2e784",
+   "metadata": {},
+   "source": [
+    "**Question 13.1**: Read the contents of `'planets_1.csv'` into a **list** of **lists** using the `process_csv` function, and extract the **header** and the **rows** in the file."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9ef2ac17",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# replace the ... with your code\n",
+    "\n",
+    "planets_1_csv = process_csv(...) # read the data in 'planets_1.csv'\n",
+    "planets_header = ...\n",
+    "planets_1_rows = ..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "331a296b",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q13-1\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "acb8b8ac",
+   "metadata": {},
+   "source": [
+    "Now, you are ready to read the data in `mapping_1.json`. Since this is a JSON file, you will need a new function to read this file:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9d9f8b0d",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# this function uses the 'load' function from the json module (already imported in this notebook) to read files\n",
+    "def read_json(path):\n",
+    "    with open(path, encoding=\"utf-8\") as f:\n",
+    "        return json.load(f)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "16439061",
+   "metadata": {},
+   "source": [
+    "**Question 13.2**: Read the contents of `'mapping_1.json'` into a **dictionary** using the `read_json` function."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "95ebd431",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# we have done this for you\n",
+    "\n",
+    "mapping_1_json = read_json(os.path.join(\"small_data\", \"mapping_1.json\"))\n",
+    "\n",
+    "mapping_1_json"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "05f1ce5c",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q13-2\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "aa4b3d64",
+   "metadata": {},
+   "source": [
+    "### Segment 3.4: Combining data from CSV and JSON files\n",
+    "\n",
+    "We are now ready to combine the data from `planets_1_rows` and `mapping_1_json` to create `Planet` objects. Before we start, it might be useful to create a function similar to `star_cell` for preprocessing the values in the CSV files."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "8c90c9a8",
+   "metadata": {},
+   "source": [
+    "### Function 2: `planet_cell(row_idx, col_name, planets_rows, header=planets_header)`\n",
+    "\n",
+    "Just like the data in `stars_1.csv`, `stars_2.csv`, and `stars_3.csv`, some of the data in `planets_1.csv`, `planets_2.csv`, and `planets_3.csv` is **missing**.  So, now would be a good time to create a function that takes in a `row_idx`, a `col_name` and a **list** of **lists** `planets_rows` (as well as the optional argument `header`) and returns the value of the column `col_name` at the row `row_idx`.\n",
+    "\n",
+    "This function **must** typecast the values it returns based on the `col_name`. If the value in `planets_rows` is missing (i.e., it is `''`), then the value returned **must** be `None`.\n",
+    "\n",
+    "The **column** of `planets_rows` where the value should be obtained from, and the correct **data type** for the value are listed in the table below:\n",
+    "\n",
+    "|Column of `planets_rows`|Data Type|\n",
+    "|------|---------|\n",
+    "|Planet Name|**string**|\n",
+    "|Discovery Year|**int**|\n",
+    "|Discovery Method|**string**|\n",
+    "|Controversial Flag|**bool**|\n",
+    "|Orbital Period [days]|**float**|\n",
+    "|Planet Radius [Earth Radius]|**float**|\n",
+    "|Planet Mass [Earth Mass]|**float**|\n",
+    "|Orbit Semi-Major Axis [au]|**float**|\n",
+    "|Eccentricity|**float**|\n",
+    "|Equilibrium Temperature [K]|**float**|\n",
+    "|Insolation Flux [Earth Flux]|**float**|\n",
+    "\n",
+    "**Important Warning:** Notice that the `Controversial Flag` column has to be converted into a **bool**. The data is stored in `planets_1.csv` (and consequently in `planets_rows`) as `\"0\"/\"1\"` values (with `\"0\"` representing `False` and `\"1\"` representing `True`). However typecasting **strings** to **bools** is not straightforward. Run the following cell and try to figure out what is happening:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "0f64fff3",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "strings = [\"0\", \"1\", \"\", \" \", \"True\", \"False\"]\n",
+    "for string in strings:\n",
+    "    print(bool(string))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "7adaed7d",
+   "metadata": {},
+   "source": [
+    "If you want to convert the **strings** into **bools**, you will have to explicitly use `if/else` statements to determine whether the value is `\"0\"` or `\"1\"`, as can be seen in the starter code below:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "1e78aa1c",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# replace the ... with your code\n",
+    "\n",
+    "def planet_cell(row_idx, col_name, planets_rows, header=planets_header):\n",
+    "    col_idx = ... # extract col_idx from col_name and header\n",
+    "    val = ... # extract the value at row_idx and col_idx\n",
+    "    if val == '':\n",
+    "        return None\n",
+    "    if col_name in [\"Controversial Flag\"]:\n",
+    "        if val == \"1\":\n",
+    "            return ...\n",
+    "        else:\n",
+    "            return ...\n",
+    "    # for all other columns typecast 'val' and return it depending on col_name"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "13800e0a",
+   "metadata": {},
+   "source": [
+    "**Question 14.1**: Use the `planet_cell` function to find the value of the column `\"Planet Name\"` of the **first** planet in `\"planets_1.csv\"`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "a4e9092d",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# we have done this one for you\n",
+    "\n",
+    "first_planet_name = planet_cell(0, 'Planet Name', planets_1_rows)\n",
+    "\n",
+    "first_planet_name"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "7ee54496",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q14-1\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "8279bd8e",
+   "metadata": {},
+   "source": [
+    "**Question 14.2**: Use the `planet_cell` function to find the value of the column `\"Insolation Flux [Earth Flux]\"` of the **first** planet in `\"planets_1.csv\"`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "a1b9933b",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# we have done this one for you\n",
+    "# do not worry if there is no output, the variable is expected to hold the value None\n",
+    "\n",
+    "first_planet_flux = planet_cell(0, 'Insolation Flux [Earth Flux]', planets_1_rows)\n",
+    "\n",
+    "first_planet_flux"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "c160864e",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q14-2\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "26c2b56c",
+   "metadata": {},
+   "source": [
+    "**Question 14.3**: Use the `planet_cell` function to find the value of the column `\"Controversial Flag\"` of the **second** planet in `\"planets_1.csv\"`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e9a372e2",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'second_planet_controversy', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "7a5ef2a0",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q14-3\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ce99a6e1",
+   "metadata": {},
+   "source": [
+    "**Question 15**: Create a `Planet` object for the **first** star in `\"planets_1.csv\"`.\n",
+    "\n",
+    "The **attribute** of the `Planet` namedtuple object, the corresponding **column** of the `planets_1.csv` file where the value should be obtained from, and the correct **data type** for the value are listed in the table below:\n",
+    "\n",
+    "|Attribute of `Planet` object|Column of `planets_1.csv`|Data Type|\n",
+    "|---------|------|---------|\n",
+    "|`planet_name`|Planet Name|**string**|\n",
+    "|`host_name`| - |**string**|\n",
+    "|`discovery_method`|Discovery Method|**string**|\n",
+    "|`discovery_year`|Discovery Year|**int**|\n",
+    "|`controversial_flag`|Controversial Flag|**bool**|\n",
+    "|`orbital_period`|Orbital Period [days]|**float**|\n",
+    "|`planet_radius`|Planet Radius [Earth Radius]|**float**|\n",
+    "|`planet_mass`|Planet Mass [Earth Mass]|**float**|\n",
+    "|`semi_major_radius`|Orbit Semi-Major Axis [au]|**float**|\n",
+    "|`eccentricity`|Eccentricity|**float**|\n",
+    "|`equilibrium_temperature`|Equilibrium Temperature [K]|**float**|\n",
+    "|`insolation_flux`|Insolation Flux [Earth Flux]|**float**|\n",
+    "\n",
+    "\n",
+    "The value of the `host_name` attribute is found in `mapping_1.json`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "a2632c39",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "planets_header"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "0dc17264",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# replace the ... with your code\n",
+    "\n",
+    "row_idx = 0 # the index of the planet we want to convert into a Planet object\n",
+    "\n",
+    "# extract the values from planets_1_rows\n",
+    "planet_name = planet_cell(row_idx, 'Planet Name', planets_1_rows)\n",
+    "host_name = mapping_1_json[planet_name]\n",
+    "discovery_method = planet_cell(row_idx, 'Discovery Method', planets_1_rows)\n",
+    "discovery_year = ...\n",
+    "controversial_flag = ...\n",
+    "orbital_period = ...\n",
+    "planet_radius = ...\n",
+    "planet_mass = ...\n",
+    "semi_major_radius = ...\n",
+    "eccentricity = ...\n",
+    "equilibrium_temperature = ...\n",
+    "insolation_flux = ...\n",
+    "\n",
+    "# initialize 'first_planet'\n",
+    "first_planet = Planet(planet_name, host_name, discovery_method, discovery_year,\\\n",
+    "                  controversial_flag, orbital_period, planet_radius, planet_mass,\\\n",
+    "                  semi_major_radius, eccentricity, equilibrium_temperature, insolation_flux)\n",
+    "\n",
+    "first_planet"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "43275a5a",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q15\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6b01aa7f",
+   "metadata": {},
+   "source": [
+    "**Question 16**: Create a **list** of `Planet` objects of each planet in `\"planets_1.csv\"`.\n",
+    "\n",
+    "Your output **must** look like this:\n",
+    "```python\n",
+    "[Planet(planet_name='55 Cnc b', host_name='55 Cnc', discovery_method='Radial Velocity', \n",
+    "        discovery_year=1996, controversial_flag=False, orbital_period=14.6516, \n",
+    "        planet_radius=13.9, planet_mass=263.9785, semi_major_radius=0.1134, eccentricity=0.0,\n",
+    "        equilibrium_temperature=700.0, insolation_flux=None),\n",
+    " Planet(planet_name='55 Cnc c', host_name='55 Cnc', discovery_method='Radial Velocity', \n",
+    "        discovery_year=2004, controversial_flag=False, orbital_period=44.3989, \n",
+    "        planet_radius=8.51, planet_mass=54.4738, semi_major_radius=0.2373, eccentricity=0.03, \n",
+    "        equilibrium_temperature=None, insolation_flux=None),\n",
+    " Planet(planet_name='DMPP-1 b', host_name='DMPP-1', discovery_method='Radial Velocity', \n",
+    "        discovery_year=2019, controversial_flag=False, orbital_period=18.57, \n",
+    "        planet_radius=5.29, planet_mass=24.27, semi_major_radius=0.1462, eccentricity=0.083, \n",
+    "        equilibrium_temperature=877.0, insolation_flux=None),\n",
+    " Planet(planet_name='GJ 876 b', host_name='GJ 876', discovery_method='Radial Velocity', \n",
+    "        discovery_year=1998, controversial_flag=False, orbital_period=61.1166, \n",
+    "        planet_radius=13.3, planet_mass=723.2235, semi_major_radius=0.208317, eccentricity=0.0324,\n",
+    "        equilibrium_temperature=None, insolation_flux=None),\n",
+    " Planet(planet_name='GJ 876 c', host_name='GJ 876', discovery_method='Radial Velocity', \n",
+    "        discovery_year=2000, controversial_flag=False, orbital_period=30.0881, \n",
+    "        planet_radius=14.0, planet_mass=226.9846, semi_major_radius=0.12959, eccentricity=0.25591, \n",
+    "        equilibrium_temperature=None, insolation_flux=None)]\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "c17389b0",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'planets_1_list', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "d76f9821",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q16\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5090b70b",
+   "metadata": {},
+   "source": [
+    "**Question 17.1**: What is the **fifth** `Planet` object in `'planets_1.csv'`?\n",
+    "\n",
+    "You **must** access from the `planets_1_list` to answer this question."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e3feb36c",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'fifth_planet', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "af5c2a76",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q17-1\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9dabec60",
+   "metadata": {},
+   "source": [
+    "**Question 17.2**: What is the `planet_name` of the **fifth** `Planet` in `'planets_1.csv'`?\n",
+    "\n",
+    "You **must** access from the `planets_1_list` to answer this question."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f2cb1d3c",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'fifth_planet_name', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e798e2c1",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q17-2\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "f0ef57a0",
+   "metadata": {},
+   "source": [
+    "**Question 17.3**: What is the `controversial_flag` of the **fourth** `Planet` in `'planets_1.csv'`?\n",
+    "\n",
+    "You **must** access from the `planets_1_list` to answer this question."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "322a8419",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'fourth_planet_controversy', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "0864d6fe",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q17-3\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9bcb57e6",
+   "metadata": {},
+   "source": [
+    "### Segment 3.5: Data Cleaning - broken CSV rows\n",
+    "\n",
+    "The code you have written worked well for reading the data in `planets_1.csv` and `mapping_1.json`. However, it will likely **not** work for `planets_2.csv` and `mapping_2.json`. This is because the file `planets_2.csv` is **broken**. For some reason, a few rows in `planets_2.csv` have their data jumbled up. This is what `planets_2.csv` looks like:\n",
+    "\n",
+    "|Planet Name|Discovery Method|Discovery Year|Controversial Flag|Orbital Period [days]|Planet Radius [Earth Radius]|Planet Mass [Earth Mass]|Orbit Semi-Major Axis [au]|Eccentricity|Equilibrium Temperature [K]|Insolation Flux [Earth Flux]|\n",
+    "|-----------|----------------|--------------|------------------|---------------------|----------------------------|------------------------|--------------------------|------------|---------------------------|----------------------------|\n",
+    "|HD 158259 b|Radial Velocity|2020|0|2.17800000|1.292|2.22000|||1478|794.22|\n",
+    "|K2-187 b|Transit|2018|0|0.77401000|1.200|1.87000|0.016400||1815||\n",
+    "|K2-187 c|Transit|2018|0|2.87151200|1.400|2.54000|0.039200||1173||\n",
+    "|K2-187 d|K2-187|Transit|2018|0|7.14958400|2.400|6.35000|0.072000||865|\n",
+    "|WASP-47 b|2012|Transit|0|4.15914920|12.640|363.60000|0.052000|0.002800|1275|534.00|\n",
+    "\n",
+    "We can see that for some reason, in the **fourth** row, the value under the column `Discovery Method` is the name of the planet's host star. This is causing all the other columns in the row to also take meaningless values.\n",
+    "\n",
+    "Similarly, in the **fifth** row, we see that the values under the columns `Discovery Method` and `Discovery Year` are swapped.\n",
+    "\n",
+    "We will call such a **row** in a CSV file where the values under a column do not match the expected format to be a **broken row**. While it is possible to sometimes extract useful data from broken rows, in this lab and in P10, we will simply **skip** broken rows.\n",
+    "\n",
+    "In order to **skip** broken rows, you should first know how to recognize a **broken row**. In general, there is no general rule that helps you identify when a row is broken. This is because CSV rows can be **broken** in all sorts of different ways. Thankfully, we don't have to write code to catch all sorts of weird cases. It will suffice for us to manually **inspect** the file `planets_2.csv`, and identify **how** the rows are broken.\n",
+    "\n",
+    "The simplest way to recognize if a row is broken is if you run into any **RunTime Errors** when you execute your code. So, one simple way to skip bad rows would be to use `try/except` blocks to avoid processing any rows that cause the code to crash.\n",
+    "\n",
+    "**Important Note:** In this dataset, as you might have already noticed, it would be **significantly harder** to detect **broken rows** where some of the numerical values are swapped (for example, `Planet Radius [Earth Radius]` and `Planet Mass [Earth Mass]`). You may **assume** that the numerical values are **not** swapped in **any** row, and that **only the rows** in which the **data types** are not as expected are **broken**."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "141bc8f7",
+   "metadata": {},
+   "source": [
+    "**Question 18**: Create a **list** of `Planet` objects of each planet in `\"planets_2.csv\"`.\n",
+    "\n",
+    "You **must** skip any broken rows in the CSV file. Your output **must** look like this:\n",
+    "```python\n",
+    "[Planet(planet_name='HD 158259 b', host_name='HD 158259', discovery_method='Radial Velocity', \n",
+    "        discovery_year=2020, controversial_flag=False, orbital_period=2.178, \n",
+    "        planet_radius=1.292, planet_mass=2.22, semi_major_radius=None, eccentricity=None, \n",
+    "        equilibrium_temperature=1478.0, insolation_flux=794.22),\n",
+    " Planet(planet_name='K2-187 b', host_name='K2-187', discovery_method='Transit', \n",
+    "        discovery_year=2018, controversial_flag=False, orbital_period=0.77401, \n",
+    "        planet_radius=1.2, planet_mass=1.87, semi_major_radius=0.0164, eccentricity=None, \n",
+    "        equilibrium_temperature=1815.0, insolation_flux=None),\n",
+    " Planet(planet_name='K2-187 c', host_name='K2-187', discovery_method='Transit', \n",
+    "        discovery_year=2018, controversial_flag=False, orbital_period=2.871512, \n",
+    "        planet_radius=1.4, planet_mass=2.54, semi_major_radius=0.0392, eccentricity=None, \n",
+    "        equilibrium_temperature=1173.0, insolation_flux=None)]\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "ca37d017",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# replace the ... with your code\n",
+    "\n",
+    "planets_2_data = ... # read planets_2.csv\n",
+    "planets_2_rows = ... # extract the rows from planets_2_data\n",
+    "mapping_2_json = ... # read mapping_2.json\n",
+    "\n",
+    "planets_2_list = []\n",
+    "for row_idx in range(len(planets_2_rows)):\n",
+    "    try:\n",
+    "        pass # replace with your code\n",
+    "        # create a Planet object and append to 'planets_2_list'\n",
+    "    except ValueError:\n",
+    "        continue\n",
+    "\n",
+    "planets_2_list"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "a9180c21",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q18\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "934f06f7",
+   "metadata": {},
+   "source": [
+    "**Important Warning:** It is considered a bad coding practice to use *bare* `try/except` blocks. This means that you should **never** write code like this:\n",
+    "\n",
+    "```python\n",
+    "try:\n",
+    "    # some code\n",
+    "except:\n",
+    "    # some other code\n",
+    "```\n",
+    "\n",
+    "If you use *bare* `try/except` blocks, your code will seemingly work even if there are bugs in there, and it can get very hard to debug. You should always **explicitly** catch for specific errors like this:\n",
+    "\n",
+    "```python\n",
+    "try:\n",
+    "    # some code\n",
+    "except ValueError:\n",
+    "    # some other code\n",
+    "except IndexError:\n",
+    "    # some other code\n",
+    "```\n",
+    "\n",
+    "This way, your code will still crash if there is some other unexpected bug in your code that needs to be fixed, and will only go to the `except` block if it runs into a `ValueError` or an `IndexError`. The starter code above already catches specifically for `ValueError`. You **must** continue this practice in P10 as well."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "72205ef0",
+   "metadata": {},
+   "source": [
+    "### Segment 3.6: Data Cleaning - broken JSON files\n",
+    "\n",
+    "So far, we have written code that can read `planets_1.csv` and `mapping_1.json`, as well as `planets_2.csv` and `mapping_2.json`. However, if you try to read `mapping_3.json`, you are likely to run into some issues. This is because the file `mapping_3.json` is **broken**. Unlike **broken** CSV files, where we only had to skip the **broken rows**, it is much harder to parse **broken JSON files**. When a JSON file is **broken**, we often have no choice but to **skip the file entirely**.\n",
+    "\n",
+    "It is also not easy to detect if a JSON file is **broken** using `if` statements. The easiest is to simply try to read the file using the `read_json` function and check if the code crashes."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e8f24551",
+   "metadata": {},
+   "source": [
+    "**Question 19**: Determine if the `'mapping_3.json'` file is **broken** using a `try/except` block."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9caede3a",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# we have done this one for you\n",
+    "\n",
+    "try:\n",
+    "    mapping_3_json = read_json(os.path.join(\"small_data\", \"mapping_3.json\"))\n",
+    "except json.JSONDecodeError:\n",
+    "    mapping_3_json = {}\n",
+    "    \n",
+    "mapping_3_json"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "956f9c83",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q19\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "f9183605",
+   "metadata": {},
+   "source": [
+    "In the above cell, note that in the `try/except` block, we specifically checked for the `json.JSONDecodeError`. This is the error that is thrown when you try to call `json.load` on a **broken** JSON file."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5cacfca1",
+   "metadata": {},
+   "source": [
+    "## Segment 4: Data Analysis\n",
+    "\n",
+    "We have now managed to read all the data in the `small_data` directory. Now is the time to test if our data structures work!"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "b81e376e",
+   "metadata": {},
+   "source": [
+    "**Question 20.1**: What is the `host_name` of the **second** planet in `'planets_2.csv'`?\n",
+    "\n",
+    "You **must** skip any broken rows. So, you can directly access from the list `planets_2_list` to answer this question."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "38e47fd7",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'second_planet_host', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "c7b544c9",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q20-1\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "47d68311",
+   "metadata": {},
+   "source": [
+    "**Question 20.2**: What is the `Star` object of the **third** planet in `'planets_2.csv'`?\n",
+    "\n",
+    "You **must** skip any broken rows. So, you can directly access from the list `planets_2_list` to answer this question.\n",
+    "\n",
+    "**Hint:** You can use the `stars_dict` **dictionary** defined in q12.2 to find the `Star` object."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "c3631118",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'third_planet_star', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "dac9696d",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q20-2\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "40e07b1f",
+   "metadata": {},
+   "source": [
+    "**Question 20.3**: What is the `stellar_radius` of the star around which the **first** planet in `'planets_1.csv'` orbits?\n",
+    "\n",
+    "You can directly access from the list `planets_1_list` to answer this question."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "794a5bb1",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'first_planet_star_radius', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "578652d9",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q20-3\")"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.13"
+  },
+  "otter": {
+   "OK_FORMAT": true,
+   "tests": {
+    "planet_object": {
+     "name": "planet_object",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"qplanet_object\", jupiter)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q1-1": {
+     "name": "q1-1",
+     "points": 2,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q1-1\", all_files)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q1-2": {
+     "name": "q1-2",
+     "points": 3,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q1-2\", actual_files)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q10-1": {
+     "name": "q10-1",
+     "points": 1,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q10-1\", first_star_type)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q10-2": {
+     "name": "q10-2",
+     "points": 2,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q10-2\", second_star_age)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q10-3": {
+     "name": "q10-3",
+     "points": 2,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q10-3\", third_star_mass)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q11": {
+     "name": "q11",
+     "points": 5,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q11\", stars_2_dict)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q12-1": {
+     "name": "q12-1",
+     "points": 2,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q12-1\", stars_3_dict)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q12-2": {
+     "name": "q12-2",
+     "points": 3,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q12-2\", stars_dict)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q13-1": {
+     "name": "q13-1",
+     "points": 3,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q13-1\", planets_1_rows)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q13-2": {
+     "name": "q13-2",
+     "points": 2,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q13-2\", mapping_1_json)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q14-1": {
+     "name": "q14-1",
+     "points": 1,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q14-1\", first_planet_name)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q14-2": {
+     "name": "q14-2",
+     "points": 2,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q14-2\", first_planet_flux)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q14-3": {
+     "name": "q14-3",
+     "points": 2,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q14-3\", second_planet_controversy)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q15": {
+     "name": "q15",
+     "points": 5,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q15\", first_planet)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q16": {
+     "name": "q16",
+     "points": 5,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q16\", planets_1_list)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q17-1": {
+     "name": "q17-1",
+     "points": 1,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q17-1\", fifth_planet)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q17-2": {
+     "name": "q17-2",
+     "points": 2,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q17-2\", fifth_planet_name)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q17-3": {
+     "name": "q17-3",
+     "points": 2,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q17-3\", fourth_planet_controversy)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q18": {
+     "name": "q18",
+     "points": 5,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q18\", planets_2_list)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q19": {
+     "name": "q19",
+     "points": 5,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q19\", mapping_3_json)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q2": {
+     "name": "q2",
+     "points": 5,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q2\", files_in_small_data)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q20-1": {
+     "name": "q20-1",
+     "points": 1,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q20-1\", second_planet_host)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q20-2": {
+     "name": "q20-2",
+     "points": 2,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q20-2\", third_planet_star)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q20-3": {
+     "name": "q20-3",
+     "points": 2,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q20-3\", first_planet_star_radius)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q3-1": {
+     "name": "q3-1",
+     "points": 1,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q3-1\", stars_1_path)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q3-2": {
+     "name": "q3-2",
+     "points": 4,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q3-2\", paths_in_small_data)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q4-1": {
+     "name": "q4-1",
+     "points": 2,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q4-1\", json_paths)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q4-2": {
+     "name": "q4-2",
+     "points": 3,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q4-2\", stars_paths)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q5": {
+     "name": "q5",
+     "points": 5,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q5\", first_star)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q6": {
+     "name": "q6",
+     "points": 5,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q6\", second_star)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q7-1": {
+     "name": "q7-1",
+     "points": 1,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q7-1\", second_star_spectral_type)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q7-2": {
+     "name": "q7-2",
+     "points": 1,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q7-2\", first_star_stellar_age)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q7-3": {
+     "name": "q7-3",
+     "points": 3,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q7-3\", stellar_radius_ratio)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q8": {
+     "name": "q8",
+     "points": 5,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q8\", stars_1_dict)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q9-1": {
+     "name": "q9-1",
+     "points": 2,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q9-1\", gj_876)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q9-2": {
+     "name": "q9-2",
+     "points": 3,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"q9-2\", gj_876_luminosity)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "star_object": {
+     "name": "star_object",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> practice_test.check(\"qstar_object\", sun)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    }
+   }
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/sum23/labs/lab10/practice_test.py b/sum23/labs/lab10/practice_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..60526c4a1eb7008efc62de466e4ecb17592d4bde
--- /dev/null
+++ b/sum23/labs/lab10/practice_test.py
@@ -0,0 +1,513 @@
+#!/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-1": (TEXT_FORMAT_UNORDERED_LIST, ['.DS_Store',
+                                                             '.ipynb_checkpoints',
+                                                             'mapping_1.json',
+                                                             'mapping_2.json',
+                                                             'mapping_3.json',
+                                                             'planets_1.csv',
+                                                             'planets_2.csv',
+                                                             'planets_3.csv',
+                                                             'stars_1.csv',
+                                                             'stars_2.csv',
+                                                             'stars_3.csv']),
+                        "1-2": (TEXT_FORMAT_UNORDERED_LIST, ['mapping_1.json',
+                                                             'mapping_2.json',
+                                                             'mapping_3.json',
+                                                             'planets_1.csv',
+                                                             'planets_2.csv',
+                                                             'planets_3.csv',
+                                                             'stars_1.csv',
+                                                             'stars_2.csv',
+                                                             'stars_3.csv']),
+                        "2": (TEXT_FORMAT_ORDERED_LIST, ['stars_3.csv',
+                                                             'stars_2.csv',
+                                                             'stars_1.csv',
+                                                             'planets_3.csv',
+                                                             'planets_2.csv',
+                                                             'planets_1.csv',
+                                                             'mapping_3.json',
+                                                             'mapping_2.json',
+                                                             'mapping_1.json']),
+                        "3-1": (TEXT_FORMAT, os.path.join("small_data", "stars_1.csv")),
+                        "3-2": (TEXT_FORMAT_ORDERED_LIST, [os.path.join("small_data", "stars_3.csv"),
+                                                            os.path.join("small_data", "stars_2.csv"),
+                                                            os.path.join("small_data", "stars_1.csv"),
+                                                            os.path.join("small_data", "planets_3.csv"),
+                                                            os.path.join("small_data", "planets_2.csv"),
+                                                            os.path.join("small_data", "planets_1.csv"),
+                                                            os.path.join("small_data", "mapping_3.json"),
+                                                            os.path.join("small_data", "mapping_2.json"),
+                                                            os.path.join("small_data", "mapping_1.json")]),
+                        "4-1": (TEXT_FORMAT_ORDERED_LIST, [os.path.join("small_data", "mapping_3.json"),
+                                                            os.path.join("small_data", "mapping_2.json"),
+                                                            os.path.join("small_data", "mapping_1.json")]),
+                        "4-2": (TEXT_FORMAT_ORDERED_LIST, [os.path.join("small_data", "stars_3.csv"),
+                                                            os.path.join("small_data", "stars_2.csv"),
+                                                            os.path.join("small_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='G8V', stellar_effective_temperature=5172.0,
+                                                            stellar_radius=0.94, stellar_mass=0.91, stellar_luminosity=-0.197,
+                                                            stellar_surface_gravity=4.43, stellar_age=10.2)),
+                        "6": (TEXT_FORMAT_NAMEDTUPLE, Star(spectral_type='F8 V', stellar_effective_temperature=6196.0,
+                                                            stellar_radius=1.26, stellar_mass=1.21, stellar_luminosity=0.32,
+                                                            stellar_surface_gravity=4.41, stellar_age=2.01)),
+                        "7-1": (TEXT_FORMAT, "F8 V"),
+                        "7-2": (TEXT_FORMAT, 10.2),
+                        "7-3": (TEXT_FORMAT, 0.7460317460317459),
+                        "7": (TEXT_FORMAT, 0.016741496598639403),
+                        "8": (TEXT_FORMAT_DICT, {'55 Cnc': Star(spectral_type='G8V', stellar_effective_temperature=5172.0,
+                                                                stellar_radius=0.94, stellar_mass=0.91, stellar_luminosity=-0.197,
+                                                                stellar_surface_gravity=4.43, stellar_age=10.2),
+                                                 'DMPP-1': Star(spectral_type='F8 V', stellar_effective_temperature=6196.0,
+                                                                stellar_radius=1.26, stellar_mass=1.21, stellar_luminosity=0.32,
+                                                                stellar_surface_gravity=4.41, stellar_age=2.01),
+                                                 'GJ 876': Star(spectral_type='M2.5V', stellar_effective_temperature=3271.0,
+                                                                stellar_radius=0.3, stellar_mass=0.32, stellar_luminosity=-1.907,
+                                                                stellar_surface_gravity=4.87, stellar_age=1.0)}),
+                        "9-1": (TEXT_FORMAT_NAMEDTUPLE, Star(spectral_type='M2.5V', stellar_effective_temperature=3271.0,
+                                                            stellar_radius=0.3, stellar_mass=0.32, stellar_luminosity=-1.907,
+                                                            stellar_surface_gravity=4.87, stellar_age=1.0)),
+                        "9-2": (TEXT_FORMAT, -1.907),
+                        "10-1": (TEXT_FORMAT, 'G0'),
+                        "10-2": (TEXT_FORMAT, None),
+                        "10-3": (TEXT_FORMAT, 1.04),
+                        "11": (TEXT_FORMAT_DICT, {'HD 158259': Star(spectral_type='G0', stellar_effective_temperature=5801.89,
+                                                                    stellar_radius=1.21, stellar_mass=1.08, stellar_luminosity=0.212,
+                                                                    stellar_surface_gravity=4.25, stellar_age=None),
+                                                 'K2-187': Star(spectral_type=None, stellar_effective_temperature=5438.0,
+                                                                stellar_radius=0.83, stellar_mass=0.97, stellar_luminosity=-0.21,
+                                                                stellar_surface_gravity=4.6, stellar_age=None),
+                                                 'WASP-47': Star(spectral_type=None, stellar_effective_temperature=5552.0,
+                                                                stellar_radius=1.14, stellar_mass=1.04, stellar_luminosity=0.032,
+                                                                stellar_surface_gravity=4.34, stellar_age=6.5)}),
+                        "12-1": (TEXT_FORMAT_DICT, {'K2-133': Star(spectral_type='M1.5 V', stellar_effective_temperature=3655.0,
+                                                                    stellar_radius=0.46, stellar_mass=0.46, stellar_luminosity=-1.479,
+                                                                    stellar_surface_gravity=4.77, stellar_age=None),
+                                                    'K2-138': Star(spectral_type='G8 V', stellar_effective_temperature=5356.3,
+                                                                    stellar_radius=0.86, stellar_mass=0.94, stellar_luminosity=-0.287,
+                                                                    stellar_surface_gravity=4.54, stellar_age=2.8),
+                                                    'GJ 667 C': Star(spectral_type='M1.5 V', stellar_effective_temperature=3350.0,
+                                                                    stellar_radius=None, stellar_mass=0.33, stellar_luminosity=-1.863,
+                                                                    stellar_surface_gravity=4.69, stellar_age=2.0)}),
+                        "12-2": (TEXT_FORMAT_DICT, {'55 Cnc': Star(spectral_type='G8V', stellar_effective_temperature=5172.0,
+                                                                    stellar_radius=0.94, stellar_mass=0.91, stellar_luminosity=-0.197,
+                                                                    stellar_surface_gravity=4.43, stellar_age=10.2),
+                                                    'DMPP-1': Star(spectral_type='F8 V', stellar_effective_temperature=6196.0,
+                                                                    stellar_radius=1.26, stellar_mass=1.21, stellar_luminosity=0.32,
+                                                                    stellar_surface_gravity=4.41, stellar_age=2.01),
+                                                    'GJ 876': Star(spectral_type='M2.5V', stellar_effective_temperature=3271.0,
+                                                                    stellar_radius=0.3, stellar_mass=0.32, stellar_luminosity=-1.907,
+                                                                    stellar_surface_gravity=4.87, stellar_age=1.0),
+                                                    'HD 158259': Star(spectral_type='G0', stellar_effective_temperature=5801.89,
+                                                                    stellar_radius=1.21, stellar_mass=1.08, stellar_luminosity=0.212,
+                                                                    stellar_surface_gravity=4.25, stellar_age=None),
+                                                    'K2-187': Star(spectral_type=None, stellar_effective_temperature=5438.0,
+                                                                    stellar_radius=0.83, stellar_mass=0.97, stellar_luminosity=-0.21,
+                                                                    stellar_surface_gravity=4.6, stellar_age=None),
+                                                    'WASP-47': Star(spectral_type=None, stellar_effective_temperature=5552.0,
+                                                                    stellar_radius=1.14, stellar_mass=1.04, stellar_luminosity=0.032,
+                                                                    stellar_surface_gravity=4.34, stellar_age=6.5),
+                                                    'K2-133': Star(spectral_type='M1.5 V', stellar_effective_temperature=3655.0,
+                                                                    stellar_radius=0.46, stellar_mass=0.46, stellar_luminosity=-1.479,
+                                                                    stellar_surface_gravity=4.77, stellar_age=None),
+                                                    'K2-138': Star(spectral_type='G8 V', stellar_effective_temperature=5356.3,
+                                                                    stellar_radius=0.86, stellar_mass=0.94, stellar_luminosity=-0.287,
+                                                                    stellar_surface_gravity=4.54, stellar_age=2.8),
+                                                    'GJ 667 C': Star(spectral_type='M1.5 V', stellar_effective_temperature=3350.0,
+                                                                    stellar_radius=None, stellar_mass=0.33, stellar_luminosity=-1.863,
+                                                                    stellar_surface_gravity=4.69, stellar_age=2.0)}),
+                        "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)),
+                        "13-1": (TEXT_FORMAT_ORDERED_LIST, [['55 Cnc b',
+                                                              'Radial Velocity',
+                                                              '1996',
+                                                              '0',
+                                                              '14.65160000',
+                                                              '13.900',
+                                                              '263.97850',
+                                                              '0.113400',
+                                                              '0.000000',
+                                                              '700',
+                                                              ''],
+                                                             ['55 Cnc c',
+                                                              'Radial Velocity',
+                                                              '2004',
+                                                              '0',
+                                                              '44.39890000',
+                                                              '8.510',
+                                                              '54.47380',
+                                                              '0.237300',
+                                                              '0.030000',
+                                                              '',
+                                                              ''],
+                                                             ['DMPP-1 b',
+                                                              'Radial Velocity',
+                                                              '2019',
+                                                              '0',
+                                                              '18.57000000',
+                                                              '5.290',
+                                                              '24.27000',
+                                                              '0.146200',
+                                                              '0.083000',
+                                                              '877',
+                                                              ''],
+                                                             ['GJ 876 b',
+                                                              'Radial Velocity',
+                                                              '1998',
+                                                              '0',
+                                                              '61.11660000',
+                                                              '13.300',
+                                                              '723.22350',
+                                                              '0.208317',
+                                                              '0.032400',
+                                                              '',
+                                                              ''],
+                                                             ['GJ 876 c',
+                                                              'Radial Velocity',
+                                                              '2000',
+                                                              '0',
+                                                              '30.08810000',
+                                                              '14.000',
+                                                              '226.98460',
+                                                              '0.129590',
+                                                              '0.255910',
+                                                              '',
+                                                              '']]),
+                        "13-2": (TEXT_FORMAT_DICT, {'55 Cnc b': '55 Cnc',
+                                                     '55 Cnc c': '55 Cnc',
+                                                     'DMPP-1 b': 'DMPP-1',
+                                                     'GJ 876 b': 'GJ 876',
+                                                     'GJ 876 c': 'GJ 876'}),
+                        "14-1": (TEXT_FORMAT, '55 Cnc b'),
+                        "14-2": (TEXT_FORMAT, None),
+                        "14-3": (TEXT_FORMAT, False),
+                        "15": (TEXT_FORMAT_NAMEDTUPLE, Planet(planet_name='55 Cnc b', host_name='55 Cnc', discovery_method='Radial Velocity',
+                                                                discovery_year=1996, controversial_flag=False, orbital_period=14.6516,
+                                                                planet_radius=13.9, planet_mass=263.9785, semi_major_radius=0.1134,
+                                                                eccentricity=0.0, equilibrium_temperature=700.0, insolation_flux=None)),
+                        "16": (TEXT_FORMAT_ORDERED_LIST, [Planet(planet_name='55 Cnc b', host_name='55 Cnc', discovery_method='Radial Velocity',
+                                                                discovery_year=1996, controversial_flag=False, orbital_period=14.6516,
+                                                                planet_radius=13.9, planet_mass=263.9785, semi_major_radius=0.1134,
+                                                                eccentricity=0.0, equilibrium_temperature=700.0, insolation_flux=None),
+                                                          Planet(planet_name='55 Cnc c', host_name='55 Cnc', discovery_method='Radial Velocity',
+                                                                discovery_year=2004, controversial_flag=False, orbital_period=44.3989,
+                                                                planet_radius=8.51, planet_mass=54.4738, semi_major_radius=0.2373,
+                                                                eccentricity=0.03, equilibrium_temperature=None, insolation_flux=None),
+                                                          Planet(planet_name='DMPP-1 b', host_name='DMPP-1', discovery_method='Radial Velocity',
+                                                                discovery_year=2019, controversial_flag=False, orbital_period=18.57,
+                                                                planet_radius=5.29, planet_mass=24.27, semi_major_radius=0.1462,
+                                                                eccentricity=0.083, equilibrium_temperature=877.0, insolation_flux=None),
+                                                          Planet(planet_name='GJ 876 b', host_name='GJ 876', discovery_method='Radial Velocity',
+                                                                discovery_year=1998, controversial_flag=False, orbital_period=61.1166,
+                                                                planet_radius=13.3, planet_mass=723.2235, semi_major_radius=0.208317,
+                                                                eccentricity=0.0324, equilibrium_temperature=None, insolation_flux=None),
+                                                          Planet(planet_name='GJ 876 c', host_name='GJ 876', discovery_method='Radial Velocity',
+                                                                discovery_year=2000, controversial_flag=False, orbital_period=30.0881,
+                                                                planet_radius=14.0, planet_mass=226.9846, semi_major_radius=0.12959,
+                                                                eccentricity=0.25591, equilibrium_temperature=None, insolation_flux=None)]),
+                        "17-1": (TEXT_FORMAT_NAMEDTUPLE, Planet(planet_name='GJ 876 c', host_name='GJ 876', discovery_method='Radial Velocity',
+                                                                discovery_year=2000, controversial_flag=False, orbital_period=30.0881,
+                                                                planet_radius=14.0, planet_mass=226.9846, semi_major_radius=0.12959,
+                                                                eccentricity=0.25591, equilibrium_temperature=None, insolation_flux=None)),
+                        "17-2": (TEXT_FORMAT, 'GJ 876 c'),
+                        "17-3": (TEXT_FORMAT, False),
+                        "18": (TEXT_FORMAT_ORDERED_LIST, [Planet(planet_name='HD 158259 b', host_name='HD 158259',
+                                                                discovery_method='Radial Velocity', discovery_year=2020,
+                                                                controversial_flag=False, orbital_period=2.178, planet_radius=1.292,
+                                                                planet_mass=2.22, semi_major_radius=None, eccentricity=None,
+                                                                equilibrium_temperature=1478.0, insolation_flux=794.22),
+                                                          Planet(planet_name='K2-187 b', host_name='K2-187', discovery_method='Transit',
+                                                                discovery_year=2018, controversial_flag=False, orbital_period=0.77401,
+                                                                planet_radius=1.2, planet_mass=1.87, semi_major_radius=0.0164,
+                                                                eccentricity=None, equilibrium_temperature=1815.0, insolation_flux=None),
+                                                          Planet(planet_name='K2-187 c', host_name='K2-187', discovery_method='Transit',
+                                                              discovery_year=2018, controversial_flag=False, orbital_period=2.871512,
+                                                              planet_radius=1.4, planet_mass=2.54, semi_major_radius=0.0392,
+                                                              eccentricity=None, equilibrium_temperature=1173.0, insolation_flux=None)]),
+                        "19": (TEXT_FORMAT_DICT, {}),
+                        "20-1": (TEXT_FORMAT, 'K2-187'),
+                        "20-2": (TEXT_FORMAT_NAMEDTUPLE, Star(spectral_type=None, stellar_effective_temperature=5438.0,
+                                                                stellar_radius=0.83, stellar_mass=0.97, stellar_luminosity=-0.21,
+                                                                stellar_surface_gravity=4.6, stellar_age=None)),
+                        "20-3": (TEXT_FORMAT, 0.94)}
+    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
diff --git a/sum23/labs/lab10/small_data.zip b/sum23/labs/lab10/small_data.zip
new file mode 100644
index 0000000000000000000000000000000000000000..8b8ce70c41ceda32004cec125292f696dcc73c63
Binary files /dev/null and b/sum23/labs/lab10/small_data.zip differ
diff --git a/sum23/projects/p10/README.md b/sum23/projects/p10/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..de80334d8f9646a803331e9c894103dfbb536dd4
--- /dev/null
+++ b/sum23/projects/p10/README.md
@@ -0,0 +1,55 @@
+# Project 10 (P10): Looking at Stars and Planets
+
+
+## Corrections and clarifications:
+
+* None yet
+
+**Find any issues?** Please create a post on Piazza
+
+
+## Instructions:
+
+This project will focus on **file handling**, **namedtuples**, **data cleaning** and **error handling**. To start, download [`p10.ipynb`](https://git.doit.wisc.edu/cdis/cs/courses/cs220/cs220-lecture-material/-/blob/main/sum23/projects/p10/p10.ipynb), [`p10_test.py`](https://git.doit.wisc.edu/cdis/cs/courses/cs220/cs220-lecture-material/-/blob/main/sum23/projects/p10/p10_test.py), and [`data.zip`](https://git.doit.wisc.edu/cdis/cs/courses/cs220/cs220-lecture-material/-/blob/main/sum23/projects/p10/data.zip).
+
+After downloading `data.zip`, make sure to extract it (using [Mac directions](http://osxdaily.com/2017/11/05/how-open-zip-file-mac/) or [Windows directions](https://support.microsoft.com/en-us/help/4028088/windows-zip-and-unzip-files)). After extracting, you should see a folder called `data`, which has the following files in it:
+
+* `mapping_1.json`
+* `mapping_2.json`
+* `mapping_3.json`
+* `mapping_4.json`
+* `mapping_5.json`
+* `stars_1.csv`
+* `stars_2.csv`
+* `stars_3.csv`
+* `stars_4.csv`
+* `stars_5.csv`
+* `planets_1.csv`
+* `planets_2.csv`
+* `planets_3.csv`
+* `planets_4.csv`
+* `planets_5.csv`
+
+You may delete `data.zip` after extracting these files from it.
+
+You will work on `p10.ipynb` and hand it in. You should follow the provided directions for each question. Questions have **specific** directions on what **to do** and what **not to do**.
+
+**Important warning:** Since P10 deals heavily with file handling, whether or not the test cases pass depends greatly on the operating system being used. Even if your code passes all tests locally, it may **fail on Gradescope** if it uses a different operating system. To avoid these issues, **follow the instructions** in P10 carefully, and **after submission, check to see that all your test cases pass on the Gradescope autograder**.
+
+------------------------------
+
+## IMPORTANT Submission instructions:
+- Review the [Grading Rubric](https://git.doit.wisc.edu/cdis/cs/courses/cs220/cs220-lecture-material/-/blob/main/sum23/projects/p10/rubric.md), to ensure that you don't lose points during code review.
+- You must **save your notebook file** before you run the cell containing **export**.
+- Login to [Gradescope](https://www.gradescope.com/) and upload the zip file into the P10 assignment.
+- If you completed the project with a **partner**, make sure to **add their name** by clicking "Add Group Member"
+in Gradescope when uploading the P10 zip file.
+       
+   <img src="images/add_group_member.png" width="400">
+
+   **Warning:** You will have to add your partner on Gradescope even if you have filled out this information in your `p10.ipynb` notebook.
+   
+- It is **your responsibility** to make sure that your project clears auto-grader tests on the Gradescope test system. Otter test results should be available in a few minutes after your submission. You should be able to see both PASS / FAIL results for the 20 test cases and your total score, which is accessible via Gradescope Dashboard (as in the image below):
+       
+    <img src="images/gradescope.png" width="400">
+- **Important:** After you submit, you **need to verify** that your code is visible on Gradescope. If you displayed the output of a large variable (such as `planets_list`) anywhere in your notebook, **we will not be able to view or grade your submission**. Make sure you don't have any large outputs in any of your cells, and verify after submission that your code can be viewed.
diff --git a/sum23/projects/p10/data.zip b/sum23/projects/p10/data.zip
new file mode 100644
index 0000000000000000000000000000000000000000..dd483ea117033cc4cf594827f0393dae1d29be41
Binary files /dev/null and b/sum23/projects/p10/data.zip differ
diff --git a/sum23/projects/p10/images/README.md b/sum23/projects/p10/images/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..535f9c403813009844c0fa2466cbc76032b57dad
--- /dev/null
+++ b/sum23/projects/p10/images/README.md
@@ -0,0 +1,7 @@
+# Images
+
+Images from p10 are stored here.
+
+```python
+
+```
diff --git a/sum23/projects/p10/images/add_group_member.png b/sum23/projects/p10/images/add_group_member.png
new file mode 100644
index 0000000000000000000000000000000000000000..402e5962e3e54ce8349f60ccfe4ce2b60840dd3b
Binary files /dev/null and b/sum23/projects/p10/images/add_group_member.png differ
diff --git a/sum23/projects/p10/images/gradescope.png b/sum23/projects/p10/images/gradescope.png
new file mode 100644
index 0000000000000000000000000000000000000000..a46c44d2a9b9b8d4b76a9721809d2e81754e946a
Binary files /dev/null and b/sum23/projects/p10/images/gradescope.png differ
diff --git a/sum23/projects/p10/p10.ipynb b/sum23/projects/p10/p10.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..96a91595e074a96efca7998da0d2d0a9cea1ffe2
--- /dev/null
+++ b/sum23/projects/p10/p10.ipynb
@@ -0,0 +1,2352 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "1fb8956e",
+   "metadata": {
+    "cell_type": "code",
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "import otter\n",
+    "# nb_name should be the name of your notebook without the .ipynb extension\n",
+    "nb_name = \"p10\"\n",
+    "py_filename = nb_name + \".py\"\n",
+    "grader = otter.Notebook(nb_name + \".ipynb\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b3c0611c",
+   "metadata": {
+    "deletable": false
+   },
+   "outputs": [],
+   "source": [
+    "import p10_test"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "f4b42f5f",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "# Project 10: Stars and Planets"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a8c01ea1",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "## Learning Objectives:\n",
+    "\n",
+    "In this project, you will demonstrate how to:\n",
+    "\n",
+    "* use `os` module to get information of files in a directory,\n",
+    "* use `os` module to get paths of files,\n",
+    "* look up data between JSON and CSV files using unique keys,\n",
+    "* read JSON and CSV files to store data to `namedTuple` objects,\n",
+    "* clean up missing values and handle cases when the file is too corrupt to parse,\n",
+    "\n",
+    "Please go through [Lab 10](https://git.doit.wisc.edu/cdis/cs/courses/cs220/cs220-lecture-material/-/tree/main/sum23/labs/lab10) before working on this project. The lab introduces some useful techniques related to this project."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "275b9124",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "## Note on Academic Misconduct:\n",
+    "\n",
+    "**IMPORTANT**: P10 and P11 are two parts of the same data analysis. You **cannot** switch project partners between these two projects. That is if you partner up with someone for P10, you have to sustain that partnership until end of P11. Now may be a good time to review [our course policies](https://canvas.wisc.edu/courses/355767/pages/syllabus?module_item_id=6048035)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "fedae2f1",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "## Testing your code:\n",
+    "\n",
+    "Along with this notebook, you must have downloaded the file `p10_test.py`. If you are curious about how we test your code, you can explore this file, and specifically the value of the variable `expected_json`, to understand the expected answers to the questions."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "86ca6379",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "## Setup:\n",
+    "\n",
+    "Before proceeding much further, download `data.zip` and extract it to a directory on your\n",
+    "computer (using [Mac directions](http://osxdaily.com/2017/11/05/how-open-zip-file-mac/) or\n",
+    "[Windows directions](https://support.microsoft.com/en-us/help/4028088/windows-zip-and-unzip-files)).\n",
+    "\n",
+    "You need to make sure that the project files are stored in the following structure:\n",
+    "\n",
+    "```\n",
+    "+-- p10.ipynb\n",
+    "+-- p10_test.py\n",
+    "+-- data\n",
+    "|   +-- .DS_Store\n",
+    "|   +-- .ipynb_checkpoints\n",
+    "|   +-- mapping_1.json\n",
+    "|   +-- mapping_2.json\n",
+    "|   +-- mapping_3.json\n",
+    "|   +-- mapping_4.json\n",
+    "|   +-- mapping_5.json\n",
+    "|   +-- planets_1.csv\n",
+    "|   +-- planets_2.csv\n",
+    "|   +-- planets_3.csv\n",
+    "|   +-- planets_4.csv\n",
+    "|   +-- planets_5.csv\n",
+    "|   +-- stars_1.csv\n",
+    "|   +-- stars_2.csv\n",
+    "|   +-- stars_3.csv\n",
+    "|   +-- stars_4.csv\n",
+    "|   +-- stars_5.csv\n",
+    "```\n",
+    "\n",
+    "Make sure that the files inside `data.zip` are inside the `data` directory. If you place your files inside some other directory, then your code will **fail on Gradescope** even after passing local tests."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "b2433d61",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "## Project Description:\n",
+    "\n",
+    "Cleaning data is an important part of a data scientist's work cycle. As you have already seen, the data we will be analyzing in P10 and P11 has been split up into 15 different files of different formats. Even worse, as you shall see later in this project, some of these files have been corrupted, and lots of data is missing. Unfortunately, in the real world, a lot of data that you will come across will be in rough shape, and it is your job to clean it up before you can analyze it. In P10, you will combine the data in these different files to create a few manageable data structures, which can be easily analyzed. In the process, you will also have to deal with broken CSV files (by skipping rows with broken data), and broken JSON files (by skipping the files entirely).\n",
+    "\n",
+    "After you create these data structures, in P11, you will dive deeper by analyzing this data and arrive at some exciting conclusions about various planets and stars outside our Solar System."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "eb71a2bf",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "## The Data:\n",
+    "\n",
+    "In P10 and P11, you will be studying stars and planets outside our Solar System using this dataset from the [NASA Exoplanet Archive](https://exoplanetarchive.ipac.caltech.edu/cgi-bin/TblView/nph-tblView?app=ExoTbls&config=PSCompPars). You will use Python to ask some interesting questions about the laws of the universe and explore the habitability of other planets in our universe. The raw data from the [NASA Exoplanet Archive](https://exoplanetarchive.ipac.caltech.edu/cgi-bin/TblView/nph-tblView?app=ExoTbls&config=PSCompPars) has been parsed and stored in multiple different files of different formats. You can find these files inside `data.zip`.\n",
+    "\n",
+    "You can open each of these files using Microsoft Excel or some other Spreadsheet viewing software to see how the data is stored. For example, these are the first three rows of the file `stars_1.csv`:\n",
+    "\n",
+    "|Name|Spectral Type|Stellar Effective Temperature [K]|Stellar Radius [Solar Radius]|Stellar Mass [Solar mass]|Stellar Luminosity [log(Solar)]|Stellar Surface Gravity [log10(cm/s**2)]|Stellar Age [Gyr]|\n",
+    "|----|-------------|---------------------------------|-----------------------------|-------------------------|-------------------------------|----------------------------------------|-----------------|\n",
+    "|11 Com|G8 III|4742.00|19.00|2.70|2.243|2.31||\n",
+    "|11 UMi|K4 III|4213.00|29.79|2.78|2.430|1.93|1.560|\n",
+    "|14 And|K0 III|4813.00|11.00|2.20|1.763|2.63|4.500|\n",
+    "\n",
+    "As you might have already guessed, this file contains data on a number of *stars* outside our solar system along with some important statistics about these stars. The columns here are as follows:\n",
+    "\n",
+    "- `Name`: The name given to the star by the International Astronomical Union,\n",
+    "- `Spectral Type`: The Spectral Classification of the star as per the Morgan–Keenan (MK) system,\n",
+    "- `Stellar Effective Temperature [K]`: The temperature of a black body (in units of Kelvin) that would emit the observed radiation of the star,\n",
+    "- `Stellar Radius [Solar Radius]`: The radius of the star (in units of the radius of the Sun),\n",
+    "- `Stellar Mass [Solar mass]`: The mass of the star (in units of the mass of the Sun),\n",
+    "- `Stellar Luminosity [log(Solar)]`: The total amount of energy radiated by the star each second (represented by the logarithm of the energy radiated by the Sun in each second),\n",
+    "- `Stellar Surface Gravity [log10(cm/s**2)]`: The acceleration due to the gravity of the Star at its surface (represented by the logarithm of the acceleration measured in centimeter per second squared),\n",
+    "- `Stellar Age [Gyr]`: The total age of the star (in units of Giga years, i.e., billions of years).\n",
+    "\n",
+    "The four other files `stars_2.csv`, `stars_3.csv`, `stars_4.csv`, and `stars_5.csv` also store similar data in the same format. At this stage of the project, it is alright if you do not understand what these columns mean - they will be explained to you when they become necessary (in P11).\n",
+    "\n",
+    "On the other hand, here are the first three rows of the file `planets_1.csv`:\n",
+    "\n",
+    "|Planet Name|Discovery Method|Discovery Year|Controversial Flag|Orbital Period [days]|Planet Radius [Earth Radius]|Planet Mass [Earth Mass]|Orbit Semi-Major Axis [au]|Eccentricity|Equilibrium Temperature [K]|Insolation Flux [Earth Flux]|\n",
+    "|-----------|----------------|--------------|------------------|---------------------|----------------------------|------------------------|---------------------------|------------|---------------------------|----------------------------|\n",
+    "|11 Com b|Radial Velocity|2007|0|326.03|12.1|6165.6|1.29|0.231|||\n",
+    "|11 UMi b|Radial Velocity|2009|0|516.21997|12.3|4684.8142|1.53|0.08|||\n",
+    "|14 And b|Radial Velocity|2008|0|185.84|12.9|1525.5|0.83|0|||\n",
+    "\n",
+    "This file contains data on a number of *planets* outside our solar system along with some important statistics about these planets. The columns here are as follows:\n",
+    "\n",
+    "- `Planet Name`: The name given to the planet by the International Astronomical Union,\n",
+    "- `Discovery Method`: The method by which the planet was discovered,\n",
+    "- `Discovery Year`: The year in which the planet was discovered,\n",
+    "- `Controversial Flag`: Indicates whether the status of the discovered object as a planet was disputed at the time of discovery, \n",
+    "- `Orbital Period [days]`: The amount of time (in units of days) it takes for the planet to complete one orbit around its star,\n",
+    "- `Planet Radius [Earth Radius]`: The radius of the planet (in units of the radius of the Earth),\n",
+    "- `Planet Mass [Earth Mass]`: The mass of the planet (in units of the mass of the Earth),\n",
+    "- `Orbit Semi-Major Axis [au]`: The semi-major axis of the planet's elliptical orbit around its host star (in units of Astronomical Units),\n",
+    "- `Eccentricity`: The eccentricity of the planet's orbit around its host star,\n",
+    "- `Equilibrium Temperature [K]`: The temperature of the planet (in units of Kelvin) if it were a black body heated only by its host star,\n",
+    "- `Insolation Flux [Earth Flux]`:  The amount of radiation the planet received from its host star per unit of area (in units of the Insolation Flux of the Earth from the Sun).\n",
+    "\n",
+    "The four other files `planets_2.csv`, `planets_3.csv`, `planets_4.csv`, and `planets_5.csv` also store similar data in the same format. At this stage of the project, it is alright if you do not understand what these columns mean - they will be explained to you when they become necessary (in P11).\n",
+    "\n",
+    "\n",
+    "Finally, if you take a look at `mapping_1.json` (you can open json files using any Text Editor), you will see that the first three entries look like this:\n",
+    "\n",
+    "```\n",
+    "{\"11 Com b\": \"11 Com\", \"11 UMi b\": \"11 UMi\", \"14 And b\": \"14 And\", ...}\n",
+    "```\n",
+    "\n",
+    "This file contains a *mapping* from each *planet* in `planets_1.csv` to the *star* in `stars_1.csv` that the planet orbits. Similarly, `mapping_2.json` contains a *mapping* from each *planet* in `planets_2.csv` to the *star* in `stars_2.csv` that the planet orbits. The pattern also holds true for `mapping_3.json`, `mapping_4.json`, and `mapping_5.json`."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d1eb59ea",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "## Project Requirements:\n",
+    "\n",
+    "You **may not** hardcode indices in your code, unless the question explicitly says to. If you open your `.csv` files with Excel, manually count through the rows and use this number to loop through the dataset, this is also considered as hardcoding. We'll **manually deduct** points from your autograder score on Gradescope during code review.\n",
+    "\n",
+    "**Store** your final answer for each question in the **variable specified for each question**. This step is important because Otter grades your work by comparing the value of this variable against the correct answer.\n",
+    "\n",
+    "For some of the questions, we'll ask you to write (then use) a function to compute the answer. If you compute the answer **without** creating the function we ask you to write, we'll **manually deduct** points from your autograder score on Gradescope, even if the way you did it produced the correct answer.\n",
+    "\n",
+    "Required Functions:\n",
+    "- `star_cell`\n",
+    "- `get_stars`\n",
+    "- `planet_cell`\n",
+    "- `get_planets`\n",
+    "\n",
+    "In this project, you will also be required to define certain **data structures**. If you do not create these data structures exactly as specified, we'll **manually deduct** points from your autograder score on Gradescope, even if the way you did it produced the correct answer.\n",
+    "\n",
+    "Required Data Structures:\n",
+    "- `Star` (**namedtuple**)\n",
+    "- `stars_dict` (**dictionary** mapping **strings** to `Star` objects)\n",
+    "- `Planet` (**namedtuple**)\n",
+    "- `planets_list` (**list** of `Planet` objects)\n",
+    "\n",
+    "In addition, you are also **required** to follow the requirements below:\n",
+    "\n",
+    "* You **must** never use the output of the `os.listdir` function directly. You **must** always first remove all files and directories that start with `\".\"`, and sort the list before doing anything with it.\n",
+    "* You are **not** allowed to use *bare* `try/except` blocks. In other words, you can **not** use `try/except` without explicitly specifying the type of exceptions that you want to catch.\n",
+    "* You are **only** allowed to use Python commands and concepts that have been taught in the course prior to the release of P10. In particular, this means that you are **not** allowed to use **modules** like `pandas` to answer the questions in this project.\n",
+    "* Please do not dsplay `start_dict` or `planets_dict` anywhere in the notebook unless explicitly mentioned. Please remove such print statements before submission.\n",
+    "\n",
+    "We will **manually deduct** points if you do **not** follow these guidelines.\n",
+    "\n",
+    "For more details on what will cause you to lose points during code review and specific requirements, please take a look at the [Grading rubric](https://git.doit.wisc.edu/cdis/cs/courses/cs220/cs220-lecture-material/-/blob/main/sum23/projects/p10/rubric.md)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e48a30c5",
+   "metadata": {
+    "deletable": false,
+    "editable": false,
+    "lines_to_next_cell": 0
+   },
+   "source": [
+    "## Questions and Functions:\n",
+    "\n",
+    "Let us start by importing all the modules we will need for this project."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "a21e9311",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# it is considered a good coding practice to place all import statements at the top of the notebook\n",
+    "# please place all your import statements in this cell if you need to import any more modules for this project\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c76cb103",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "### File handling:\n",
+    "\n",
+    "In the next questions, you will be using functions in the `os` module to make **lists** of files and paths in the `data` directory. All your **lists** **must** satisfy the following conditions:\n",
+    "\n",
+    "* Any files with names beginning with `\".\"` **must** be **excluded**.\n",
+    "* The list **must** be in **reverse-alphabetical** order."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "b4bd1260",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 1:** What are the **names** of the files present in the `data` directory\n",
+    "\n",
+    "Your output **must** be a **list** of **strings** representing the **names** of the files."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "008004e3",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'files_in_data', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "c95d46e9",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q1\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "26a178ec",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 2:** What are the **paths** of all the files in the `data` directory?\n",
+    "\n",
+    "Your output **must** be a **list** of **strings** representing the **paths** of the files. You **must** use the `files_in_data` variable created in the previous question to answer this.\n",
+    "\n",
+    "**Warning:** Please **do not hardcode** `\"/\"` or `\"\\\"` in your path because doing so will cause your function to **fail** on a computer that's not using the same operating system as yours. This may result in your code failing on Gradescope."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "0145dbc4",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'file_paths', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "68eb1e38",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q2\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "781e1b4e",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 3:** What are the **paths** of all the **CSV files** present in `data` directory?\n",
+    "\n",
+    "Your output **must** be filtered to **only** include files ending in `.csv`. You **must** use either the `files_in_data` or `file_paths` variables created in the previous questions to answer this.\n",
+    "\n",
+    "**Warning:** Please **do not hardcode** `\"/\"` or `\"\\\"` in your path because doing so will cause your function to **fail** on a computer that's not using the same operating system as yours. This may result in your code failing on Gradescope."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f04ef7d3",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'csv_file_paths', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "3ee22f17",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q3\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e80ed130",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 4:** What are the **paths** of all the files present in `data` directory, that **begin** with the phrase `'stars'`?\n",
+    "\n",
+    "Your output **must** be filtered to **only** include files start with `stars`. You **must** use either the `files_in_data` or `file_paths` variables created in the previous questions to answer this.\n",
+    "\n",
+    "**Warning:** Please **do not hardcode** `\"/\"` or `\"\\\"` in your path because doing so will cause your function to **fail** on a computer that's not using the same operating system as yours. This may result in your code failing on Gradescope."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f1bc89f4",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'stars_paths', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "37f723d2",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q4\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "48dbcd85",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "### Data Structure 1: namedtuple `Star`\n",
+    "\n",
+    "You will be using named tuples to store the data in the `stars_1.csv`, ..., `stars_5.csv` files. Before you start reading these files however, you **must** create a new `Star` type (using namedtuple). It **must** have the following attributes:\n",
+    "\n",
+    "* `spectral_type`,\n",
+    "* `stellar_effective_temperature`,\n",
+    "* `stellar_radius`,\n",
+    "* `stellar_mass`,\n",
+    "* `stellar_luminosity`,\n",
+    "* `stellar_surface_gravity`,\n",
+    "* `stellar_age`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e427a4a4",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# define the namedtuple 'Star' here\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "56b33e7c",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# run this following cell to initialize and test an example Star object\n",
+    "# if this cell fails to execute, you have likely not defined the namedtuple 'Star' correctly\n",
+    "\n",
+    "sun = Star('G2 V', 5780.0, 1.0, 1.0, 0.0, 4.44, 4.6)\n",
+    "\n",
+    "sun"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "bf4eb124",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"star_object\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "906a4ad4",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "### Creating `Star` objects\n",
+    "\n",
+    "Now that we have created the `Star` namedtuple, our next objective will be to read the files `stars_1.csv`, ..., `stars_5.csv` and create `Star` objects out of all the stars in there. In order to process the CSV files, you will first need to copy/paste the `process_csv` function you have been using since P6."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "04b3a1a1",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# copy & paste the process_csv file from previous projects here\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "cf37a6d1",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "You are now ready to read the data in `stars_1.csv` using `process_csv` and convert the data into `Star` objects. In the cell below, you **must** read the data in `stars_1.csv` and extract the **header** and the non-header **rows** of the file."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "090501e2",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# replace the ... with your code\n",
+    "\n",
+    "stars_1_csv = ... # read the data in 'stars_1.csv'\n",
+    "stars_header = ...\n",
+    "stars_1_rows = ..."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "609d8e94",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "stars_header"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ada5e28c",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "If you wish to **verify** that you have read the file and defined the variables correctly, you can check that `stars_header` has the value:\n",
+    "\n",
+    "```python\n",
+    "['Name', 'Spectral Type', 'Stellar Effective Temperature [K]', 'Stellar Radius [Solar Radius]',\n",
+    " 'Stellar Mass [Solar mass]', 'Stellar Luminosity [log(Solar)]', \n",
+    " 'Stellar Surface Gravity [log10(cm/s**2)]', 'Stellar Age [Gyr]']\n",
+    "```\n",
+    "\n",
+    "and that `stars_1_rows` has **1551** rows of which the **first three** are:\n",
+    "\n",
+    "```python\n",
+    "[['11 Com', 'G8 III', '4742.00', '19.00', '2.70', '2.243', '2.31', ''],\n",
+    " ['11 UMi', 'K4 III', '4213.00', '29.79', '2.78', '2.430', '1.93', '1.560'],\n",
+    " ['14 And', 'K0 III', '4813.00', '11.00', '2.20', '1.763', '2.63', '4.500']]\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "fa52057a",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "### Function 1: `star_cell(row_idx, col_name, stars_rows, header=stars_header)`\n",
+    "\n",
+    "This function **must** read the **list** of **lists** `stars_rows`, and extract the value at **row** index `row_idx` and **column** index `col_idx`. The function **must** typecast the value based on `col_name`. If the value in `stars_rows` is **missing** (i.e., it is `''`), then the value returned **must** be `None`.\n",
+    "\n",
+    "The **column** of `stars_rows` where the value should be obtained from, and the correct **data type** for the value are listed in the table below:\n",
+    "\n",
+    "|Column of `stars_rows`|Data Type|\n",
+    "|------|---------|\n",
+    "|Name|**string**|\n",
+    "|Spectral Type|**string**|\n",
+    "|Stellar Effective Temperature [K]|**float**|\n",
+    "|Stellar Radius [Solar Radius]|**float**|\n",
+    "|Stellar Mass [Solar mass]|**float**|\n",
+    "|Stellar Luminosity [log(Solar)]|**float**|\n",
+    "|Stellar Surface Gravity [log10(cm/s**2)]|**float**|\n",
+    "|Stellar Age [Gyr]|**float**|\n",
+    "\n",
+    "You are **allowed** to copy/paste this function from Lab-P10."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f1c3fb1c",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# define the 'star_cell' function here\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d9d9774d",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 5:** Create a `Star` object for the **third** star in `\"stars_1.csv\"`.\n",
+    "\n",
+    "You **must** access the values in `stars_1.csv` using the `star_cell` function. Note that the third star would be at **index** 2.\n",
+    "\n",
+    "The **attribute** of the `Star` namedtuple object, the corresponding **column** of the `stars_1.csv` file where the value should be obtained from, and the correct **data type** for the value are listed in the table below:\n",
+    "\n",
+    "|Attribute of `Star` object|Column of `stars_1.csv`|Data Type|\n",
+    "|---------|------|---------|\n",
+    "|`spectral_type`|Spectral Type|**string**|\n",
+    "|`stellar_effective_temperature`|Stellar Effective Temperature [K]|**float**|\n",
+    "|`stellar_radius`|Stellar Radius [Solar Radius]|**float**|\n",
+    "|`stellar_mass`|Stellar Mass [Solar mass]|**float**|\n",
+    "|`stellar_luminosity`|Stellar Luminosity [log(Solar)]|**float**|\n",
+    "|`stellar_surface_gravity`|Stellar Surface Gravity [log10(cm/s**2)]|**float**|\n",
+    "|`stellar_age`|Stellar Age [Gyr]|**float**|"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "703a66e7",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    " # compute and store the answer in the variable 'third_star', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "2760ff2e",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q5\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c6c130bc",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "### Function 2:  `get_stars(star_file)`\n",
+    "\n",
+    "This function **must** take in as its input, the path of a CSV file `star_file` which contains data on stars in the same format as `stars_1.csv`. It **must** return a **dictionary** mapping the `Name` of each star in `star_file` to a `Star` object containing all the other details of the star.\n",
+    "\n",
+    "You **must** access the values in `stars_file` using the `star_cell` function.\n",
+    "\n",
+    "You **must not** hardcode the name of the directory `data` into the `get_stars` function. Instead, you must pass it as a part of the argument `star_file`, by including it in the **path** `star_file`.\n",
+    "\n",
+    "Once again, as a reminder, the attributes of the `Star` objects should be obtained from the **rows** of `star_file` and stored as follows:\n",
+    "\n",
+    "|Attribute of `Star` object|Column of `star_file`|Data Type|\n",
+    "|---------|------|---------|\n",
+    "|`spectral_type`|Spectral Type|**string**|\n",
+    "|`stellar_effective_temperature`|Stellar Effective Temperature [K]|**float**|\n",
+    "|`stellar_radius`|Stellar Radius [Solar Radius]|**float**|\n",
+    "|`stellar_mass`|Stellar Mass [Solar mass]|**float**|\n",
+    "|`stellar_luminosity`|Stellar Luminosity [log(Solar)]|**float**|\n",
+    "|`stellar_surface_gravity`|Stellar Surface Gravity [log10(cm/s**2)]|**float**|\n",
+    "|`stellar_age`|Stellar Age [Gyr]|**float**|\n",
+    "\n",
+    "In case any data in `star_file` is **missing**, the corresponding value should be `None`.\n",
+    "\n",
+    "For example, when this function is called with the file `stars_1.csv` as the input, the **dictionary** returned should look like:\n",
+    "\n",
+    "```python\n",
+    "{'11 Com': Star(spectral_type='G8 III', stellar_effective_temperature=4742.0, \n",
+    "                stellar_radius=19.0, stellar_mass=2.7, stellar_luminosity=2.243, \n",
+    "                stellar_surface_gravity=2.31, stellar_age=None),\n",
+    " '11 UMi': Star(spectral_type='K4 III', stellar_effective_temperature=4213.0, \n",
+    "                stellar_radius=29.79, stellar_mass=2.78, stellar_luminosity=2.43, \n",
+    "                stellar_surface_gravity=1.93, stellar_age=1.56),\n",
+    " '14 And': Star(spectral_type='K0 III', stellar_effective_temperature=4813.0, \n",
+    "                stellar_radius=11.0, stellar_mass=2.2, stellar_luminosity=1.763, \n",
+    "                stellar_surface_gravity=2.63, stellar_age=4.5),\n",
+    " ...\n",
+    "}\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "66c553c3",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# define the function 'get_stars' here\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "add5372a",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# you can now use 'get_stars' to read the data in 'stars_1.csv'\n",
+    "\n",
+    "stars_1_dict = ..."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "18f1045c",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 6:** What is the `Star` object of the star (in `stars_1.csv`) named *DP Leo*?\n",
+    "\n",
+    "You **must** access the `Star` object in `stars_1_dict` **dictionary** defined above to answer this question."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "0873f43f",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'dp_leo', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "0a969f1f",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q6\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "1b7e5192",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 7:** What's the **average** `stellar_luminosity` of **all** the stars in the `star_1.csv` file?\n",
+    "\n",
+    "You **must** use the `stars_1_dict` **dictionary** defined above to answer this question.\n",
+    "\n",
+    "To find the average, you **must** first **add** up the `stellar_luminosity` value of all the stars and **divide** by the total **number** of stars. You **must skip** stars which don't have the `stellar_luminosity` data. Such stars should not contribute to either the sum of `stellar_luminosity` or to the number of stars."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "d18e6051",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'avg_lum_stars_1', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b953e06e",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q7\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4b355502",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 8:** What is the **average** `stellar_age` of **all** the stars in the `stars_2.csv` file?\n",
+    "\n",
+    "You **must** use the function `get_stars(csv_file)` to read the data in `stars_2.csv`. Your output **must** be a **float** representing the `stellar_age` in units of *gigayears*. You **must** skip stars which have missing `stellar_age` data."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "0aee52de",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'avg_age_stars_2', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "3b014793",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q8\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "61810755",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "### Data Structure 2: `stars_dict`\n",
+    "\n",
+    "You are now ready to read all the data about the stars stored in the `data` directory. You **must** now create a **dictionary** mapping the `Name` of each star in the `data` directory (inside the files `stars_1.csv`, ..., `stars_5.csv`) to the `Star` object containing all the other details about the star.\n",
+    "\n",
+    "You **must not** hardcode the files/paths of the files `stars_1.csv`, ..., `stars_5.csv` to answer this question. Instead, you **must** use the `stars_paths` variable defined earlier in Question 4 to get the list of paths needed for this question. You can use the `update` dictionary **method** to combine two **dictionaries**.\n",
+    "\n",
+    "You must use this dictionary to answer the next 3 questions."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "c7010833",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# define the variable 'stars_dict' here,\n",
+    "# but do NOT display the variable at the end\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "049120b6",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "If you wish to **verify** that you have read the files and defined `stars_dict` correctly, you can check that `stars_dict` has **3980** key/value pairs in it."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ef6c626a",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 9:** What is the `stellar_effective_temperature` of the star *Kepler-220*?\n",
+    "\n",
+    "You **must** access the correct `Star` object in the `stars_dict` **dictionary** defined above to answer this question."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "124c197b",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'kepler_220_temp', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "5aacf6bc",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q9\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e792dfb2",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 10:** Find the **name** of the **largest** star (in terms of `stellar_radius`) in the `data` directory.\n",
+    "\n",
+    "Your output **must** be a **string**. You do **not** need to worry about any ties. You **must** skip any stars with **missing** `stellar_radius` data."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "caf23326",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'biggest_star', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "602f2313",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q10\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "953cff2f",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 11:** What is the **average** `stellar_age` (in gigayears) of **all** the stars in the `data` directory whose names **start with** `\"Kepler\"`?\n",
+    "\n",
+    "Your output **must** be a **float**. You **must** skip all stars with **missing** `stellar_age` data. Such stars should not contribute to either the sum of `stellar_age` or to the number of stars."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9916d6cf",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'avg_age_kepler', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "929f63aa",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q11\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "0c580619",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "### Data Structure 3: namedtuple `Planet`\n",
+    "\n",
+    "Just as you did with the stars, you will be using named tuples to store the data about the planets in the `planets_1.csv`, ..., `planets_5.csv` files. Before you start reading these files however, you **must** create a new `Planet` type (using namedtuple). It **must** have the following attributes:\n",
+    "\n",
+    "* `planet_name`,\n",
+    "* `host_name`,\n",
+    "* `discovery_method`,\n",
+    "* `discovery_year`,\n",
+    "* `controversial_flag`,\n",
+    "* `orbital_period`,\n",
+    "* `planet_radius`,\n",
+    "* `planet_mass`,\n",
+    "* `semi_major_radius`,\n",
+    "* `eccentricity`,\n",
+    "* `equilibrium_temperature`\n",
+    "* `insolation_flux`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f33b8dd7",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# define the namedtuple 'Planet' here\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "206f8045",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# run this following cell to initialize and test an example Planet object\n",
+    "# if this cell fails to execute, you have likely not defined the namedtuple 'Planet' correctly\n",
+    "jupiter = Planet('Jupiter', 'Sun', 'Imaging', 1610, False, 4333.0, 11.209, 317.828, 5.2038, 0.0489, 110, 0.0345)\n",
+    "\n",
+    "jupiter"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "4aa03b43",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"planet_object\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "2c1555ac",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "### Creating `Planet` objects\n",
+    "\n",
+    "We are now ready to read the files in the `data` directory and create `Planet` objects. Creating `Planet` objects however, is going to be more difficult than creating `Star` objects, because the data required to create a single `Planet` object is split up into different files.\n",
+    "\n",
+    "The `planets_1.csv`, ..., `planets_5.csv` files contain all the data required to create `Planet` objects **except** for the `host_name`. The `host_name` for each planet is to be found in the `mapping_1.json`, ..., `mapping_5.json` files."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "294645a0",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "First, let us read the data in `planets_1.csv`. Since this is a CSV file, you can use the `process_csv` function from above to read this file. In the cell below, you **must** read the data in `planets_1.csv` and extract the **header** and the non-header **rows** of the file."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "69f00541",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# replace the ... with your code\n",
+    "\n",
+    "planets_1_csv = process_csv(...) # read the data in 'planets_1.csv'\n",
+    "planets_header = ...\n",
+    "planets_1_rows = ..."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "0be36ba7",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "If you wish to **verify** that you have read the file and defined the variables correctly, you can check that `planets_header` has the value:\n",
+    "\n",
+    "```python\n",
+    "['Planet Name', 'Discovery Method', 'Discovery Year', 'Controversial Flag', 'Orbital Period [days]',\n",
+    " 'Planet Radius [Earth Radius]', 'Planet Mass [Earth Mass]', 'Orbit Semi-Major Axis [au]',\n",
+    " 'Eccentricity', 'Equilibrium Temperature [K]', 'Insolation Flux [Earth Flux]']\n",
+    "```\n",
+    "\n",
+    "and that `planets_1_rows` has **1551** rows of which the **first three** are:\n",
+    "\n",
+    "```python\n",
+    "[['11 Com b', 'Radial Velocity', '2007', '0', '326.03', '12.1', '6165.6', '1.29', '0.231', '', ''],\n",
+    " ['11 UMi b', 'Radial Velocity', '2009', '0', '516.21997', '12.3', '4684.8142', '1.53', '0.08', '', ''],\n",
+    " ['14 And b', 'Radial Velocity', '2008', '0', '185.84', '12.9', '1525.5', '0.83', '0', '', '']]\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "8670a50a",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "Now, you are ready to read the data in `mapping_1.json`. Since this is a JSON file, you will need to copy/paste the `read_json` function Lab-P10, and use it to read the file."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e55e27c2",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# copy & paste the read_json file from Lab-P10\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "356b7eda",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# now use the read_json function to read 'mapping_1.json'\n",
+    "\n",
+    "mapping_1_json = ..."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d75f4b45",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "If you wish to **verify** that you have read the file correctly, you can check that `mapping_1_json` has the value:\n",
+    "\n",
+    "```python\n",
+    "{'11 Com b': '11 Com',\n",
+    " '11 UMi b': '11 UMi',\n",
+    " '14 And b': '14 And',\n",
+    " ...\n",
+    " }\n",
+    "```\n",
+    "\n",
+    "Now that we have read `planets_1.csv` and `mapping_1.json`, we are now ready to combine these two files to create `Planet` objects."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "be3275c3",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "### Function 3: `planet_cell(row_idx, col_name, planets_rows, header=planets_header)`\n",
+    "\n",
+    "This function **must** read the **list** of **lists** `planets_rows`, and extract the value at **row** index `row_idx` and **column** index `col_idx`. The function **must** typecast the value based on `col_name`. If the value in `planets_rows` is **missing** (i.e., it is `''`), then the value returned **must** be `None`.\n",
+    "\n",
+    "The **column** of `planets_rows` where the value should be obtained from, and the correct **data type** for the value are listed in the table below:\n",
+    "\n",
+    "|Column of `planets_rows`|Data Type|\n",
+    "|------|---------|\n",
+    "|Planet Name|**string**|\n",
+    "|Discovery Year|**int**|\n",
+    "|Discovery Method|**string**|\n",
+    "|Controversial Flag|**bool**|\n",
+    "|Orbital Period [days]|**float**|\n",
+    "|Planet Radius [Earth Radius]|**float**|\n",
+    "|Planet Mass [Earth Mass]|**float**|\n",
+    "|Orbit Semi-Major Axis [au]|**float**|\n",
+    "|Eccentricity|**float**|\n",
+    "|Equilibrium Temperature [K]|**float**|\n",
+    "|Insolation Flux [Earth Flux]|**float**|\n",
+    "\n",
+    "**Important Hint:** While computing the value of the attribute `controversial_flag`, note that the `Controversial Flag` column of `planets_1.csv` represents `True` with `'1'` and `False` with `'0'`. You **must** be careful with typecasting **strings** to **booleans**."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "3aacd13c",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# define the function 'planet_cell' here\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5019620d",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 12:** Create a `Planet` object for the **fifth** planet in the `planets_1.csv` file.\n",
+    "\n",
+    "You **must** access the values in `planets_1.csv` using the `planet_cell` function. Note that the fifth planet would be at **index** 4.\n",
+    "\n",
+    "The **attribute** of the `Planet` namedtuple object, the corresponding **column** of the `planets_1.csv` file where the value should be obtained from, and the correct **data type** for the value are listed in the table below:\n",
+    "\n",
+    "|Attribute of `Planet` object|Column of `planets_1.csv`|Data Type|\n",
+    "|---------|------|---------|\n",
+    "|`planet_name`|Planet Name|**string**|\n",
+    "|`host_name`| - |**string**|\n",
+    "|`discovery_method`|Discovery Method|**string**|\n",
+    "|`discovery_year`|Discovery Year|**int**|\n",
+    "|`controversial_flag`|Controversial Flag|**bool**|\n",
+    "|`orbital_period`|Orbital Period [days]|**float**|\n",
+    "|`planet_radius`|Planet Radius [Earth Radius]|**float**|\n",
+    "|`planet_mass`|Planet Mass [Earth Mass]|**float**|\n",
+    "|`semi_major_radius`|Orbit Semi-Major Axis [au]|**float**|\n",
+    "|`eccentricity`|Eccentricity|**float**|\n",
+    "|`equilibrium_temperature`|Equilibrium Temperature [K]|**float**|\n",
+    "|`insolation_flux`|Insolation Flux [Earth Flux]|**float**|\n",
+    "\n",
+    "\n",
+    "The value of the `host_name` attribute is found in `mapping_1.json`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f53440f3",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'fifth_planet', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b6306f29",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q12\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4dcb41e7",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "### Function 4: `get_planets(planet_file, mapping_file)`: \n",
+    "\n",
+    "This function **must** take in as its input, a CSV file `planet_file` which contains data on planets in the same format as `planets_1.csv`, as well as a JSON file `mapping_file` which maps planets in `planet_file` to their host star in the same format as `mapping_1.json`. This function **must** return a **list** of `Planet` objects by combining the data in these two files. The `Planet` objects **must** appear in the same order as they do in `planet_file`.\n",
+    "\n",
+    "You **must** access the values in `planets_file` using the `planet_cell` function.\n",
+    "\n",
+    "You **must not** hardcode the name of the directory `data` into the `get_planets` function. Instead, you must pass it as a part of the arguments `get_planets` and `mapping_file`.\n",
+    "\n",
+    "Once again, as a reminder, the attributes of the `Planet` objects should be obtained from the **rows** of `planet_file` and from `mapping_file` and stored as follows:\n",
+    "\n",
+    "|Attribute of `Planet` object|Column of `planets_1.csv`|Data Type|\n",
+    "|---------|------|---------|\n",
+    "|`planet_name`|Planet Name|**string**|\n",
+    "|`host_name`| - |**string**|\n",
+    "|`discovery_method`|Discovery Method|**string**|\n",
+    "|`discovery_year`|Discovery Year|**int**|\n",
+    "|`controversial_flag`|Controversial Flag|**bool**|\n",
+    "|`orbital_period`|Orbital Period [days]|**float**|\n",
+    "|`planet_radius`|Planet Radius [Earth Radius]|**float**|\n",
+    "|`planet_mass`|Planet Mass [Earth Mass]|**float**|\n",
+    "|`semi_major_radius`|Orbit Semi-Major Axis [au]|**float**|\n",
+    "|`eccentricity`|Eccentricity|**float**|\n",
+    "|`equilibrium_temperature`|Equilibrium Temperature [K]|**float**|\n",
+    "|`insolation_flux`|Insolation Flux [Earth Flux]|**float**|\n",
+    "\n",
+    "The value of the `host_name` attribute is found in `mapping_file`.\n",
+    "\n",
+    "In case any data in `planet_file` is **missing**, the corresponding value should be `None`.\n",
+    "\n",
+    "For example, when this function is called with the file `planets_1.csv` and `mapping_1.json` as the input, the **list** returned should look like:\n",
+    "\n",
+    "```python\n",
+    "[ Planet(planet_name='11 Com b', host_name='11 Com', discovery_method='Radial Velocity', discovery_year=2007, controversial_flag=False, orbital_period=326.03, planet_radius=12.1, planet_mass=6165.6, semi_major_radius=1.29, eccentricity=0.231, equilibrium_temperature=None, insolation_flux=None), \n",
+    "  Planet(planet_name='11 UMi b', host_name='11 UMi', discovery_method='Radial Velocity', discovery_year=2009, controversial_flag=False, orbital_period=516.21997, planet_radius=12.3, planet_mass=4684.8142, semi_major_radius=1.53, eccentricity=0.08, equilibrium_temperature=None, insolation_flux=None),\n",
+    " ....]\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "2ba7c70e",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "def get_planets(planet_file, mapping_file):\n",
+    "    # TODO: read planet_file to a list of lists\n",
+    "    # TODO: extract the header and rows from planet_file\n",
+    "    # TODO: read mapping_file to a dictionary\n",
+    "    # TODO: loop through each row in planet_file with indices\n",
+    "    # TODO: create a Planet object (namedTuple) for each row\n",
+    "    # TODO: add each Planet objet to a list\n",
+    "    # TODO: return the list after the end of the loop\n",
+    "    pass # replace with your code"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a5f7e17e",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 13:** What are the **last five** `Planet` objects in the **list** returned by `get_planets` when `planet_file` is `planets_1.csv` and `mapping_file` is `mapping_1.json`?\n",
+    "\n",
+    "Your output **must** be a **list** of `Planet` objects.\n",
+    "\n",
+    "**Hint:** First, you **must** use the `get_planets` function to parse the data in `planets_1.csv` and `mapping_1.json` and create a **list** of `Planet` objects. Then, you may slice this **list** to get the last five `Planet` objects."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "abe8205f",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'last_five_planets_1', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "5d8b46e1",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q13\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "0c59b28b",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 14:** What are the `Planet` objects whose `controversial_flag` attribute is `True` in the **list** returned by `get_planets` when `planet_file` is `planets_2.csv` and `mapping_file` is `mapping_2.json`?\n",
+    "\n",
+    "Your output **must** be a **list** of `Planet` objects."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b5688f2d",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'controversial_planets', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "8074ff1d",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q14\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4384e90b",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "### Data Cleaning 1: Broken CSV rows\n",
+    "\n",
+    "Our function `get_planets` works very well so far. However, it is likely that it will not work on all the files in the `data` directory. For example, if you use the function `get_planets` to read the data in `planets_4.csv` and `mapping_4.json`, you will most likely run into an error. **Try it yourself to verify!**\n",
+    "\n",
+    "The reason your code likely crashed is because there the file `planets_4.csv` is **broken**. For some reason, several rows in `planets_4.csv` have their data jumbled up. For example, in the **566**th row of `planets_4.csv`, we come across this row:\n",
+    "\n",
+    "|Planet Name|Discovery Method|Discovery Year|Controversial Flag|Orbital Period [days]|Planet Radius [Earth Radius]|Planet Mass [Earth Mass]|Orbit Semi-Major Axis [au]|Eccentricity|Equilibrium Temperature [K]|Insolation Flux [Earth Flux]|\n",
+    "|-----------|----------------|--------------|------------------|---------------------|----------------------------|------------------------|---------------------------|------------|---------------------------|----------------------------|\n",
+    "|pi Men c|pi Men|Transit|2018|0|6.26790772|2.060|3.63000|0.068647|0.076939|1170|\n",
+    "\n",
+    "We can see that for some reason, the value under the column `Discovery Method` is the name of the planet's host star. This causes the value under the column `Discovery Year` to be a **string** instead of a number.\n",
+    "\n",
+    "We will call such a **row** in a CSV file where the values under a column do not match the expected format to be a **broken row**. While it is possible to sometimes extract useful data from broken rows, in this project, we will simply **skip** broken rows.\n",
+    "\n",
+    "You **must** now go back to your definition of `get_planets` and edit it, so that any **broken rows get skipped**.\n",
+    "\n",
+    "**Hints:**\n",
+    "\n",
+    "1. The simplest way to recognize if a row is broken is if you run into any **RunTime Errors** when you call the `get_planets` function. So, one simple way to skip bad rows would be to use `try/except` blocks to avoid processing any rows that cause the code to crash; remember **not** to use *bare* `try/except` blocks.\n",
+    "2. There are only **5** broken rows in `planets_4.csv`, and they are all bunched up at the very end. You can manually **inspect** these rows, and figure out why these rows are broken.\n",
+    "\n",
+    "**Important Warning:** You are **not** allowed to **hardcode** the indices of the broken rows. You may inspect `planets_4.csv` to identify how to tell a **broken row** apart. Therefore, to use the example of the **broken row** above, you **may not** hardcode to skip the **566**th row of `planets_4.csv`. However, it is **acceptable** to make your function **skip** any row for which the value under the `Discovery Year` is not numeric, by observing that this is the reason why the row is broken."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c8151f63",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 15:** What are the **last five** `Planet` objects produced by `get_planets` when `planet_file` is `planets_4.csv` and `mapping_file` is `mapping_4.json`?\n",
+    "\n",
+    "Your output **must** be a **list** of `Planet` objects."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "1298328f",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'last_five_planets_4', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "3ab83a97",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q15\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "fe90a14d",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "### Data Cleaning 2: Broken JSON files\n",
+    "\n",
+    "We are now ready to read **all** the files in the `data` directory and create a **list** of `Planet` objects for all the planets in the directory. However, if you try to use `get_planets` on all the planet CSV files and mapping JSON files, you will likely run into another error. **Try it for yourself by calling `get_planets` on all the files in `data`!**\n",
+    "\n",
+    "It is likely that your code crashed when you tried to read the data in `planets_5.csv` and `mapping_5.json`. This is because the file `mapping_5.json` is **broken**. Unlike **broken** CSV files, where we only had to skip the **broken rows**, it is much harder to parse **broken JSON files**. When a JSON file is **broken**, we often have no choice but to **skip the file entirely**.\n",
+    "\n",
+    "You **must** now go back to your definition of `get_planets` and edit it, so that if the JSON file is **broken**, then the file is completely skipped, and only an **empty list** is returned.\n",
+    "\n",
+    "**Important Warning:** You are **not** allowed to **hardcode** the name of the files to be skipped. You **must** use `try/except` blocks to determine whether the JSON file is **broken** and skip the file if it is.\n",
+    "\n",
+    "**Hint:** You might also want to review the **Project Requirements** at the start of this project, before you use `try/except`."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "51d5afdc",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "### Data Structure 4: `planets_list`\n",
+    "\n",
+    "You are now ready to read all the data about the planets stored in the `data` directory. You **must** now create a **list** containing `Planet` objects by parsing the data inside the files `planets_1.csv`, ..., `planets_5.csv` and `mapping_1.json`, ..., `mapping_5.json`.\n",
+    "\n",
+    "You **must** skip any **broken rows** in the CSV file, and also completely skip any **broken JSON files**. However, you are **not** allowed to **hardcode** the file you need to skip. You **must** call `get_planet` on **all** 5 pairs of files to answer this question.\n",
+    "\n",
+    "You **must** use the `get_planets` function on each of the five pairs of files in the `data` directory to create `planets_list`.\n",
+    "\n",
+    "**Warning:** Recall that the ordering of the files returned by the `os.listdir` function **depends on the operating system**. So, you need to be careful if your code relies on the ordering of the **list** returned by this function. One way to avoid any issues here would be to **sort** the **list** first, so that the ordering is identical across all operating systems."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "903fcc99",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# define the variable 'planets_list' here,\n",
+    "# but do NOT display the variable at the end\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c957b1e2",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "If you wish to **verify** that you have read the files and defined `planets_list` correctly, you can check that `planets_list` has **4828** `Planet` objects in it. If it contains fewer or a greater number of planets, it is possible that you have accidentally parsed a broken CSV row in `planets_4.csv`, or accidentally parsed data from the broken JSON file `mapping_5.json`."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "72aa173e",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 16:** What is the output of `planets_list[4520:4525]`?\n",
+    "\n",
+    "Your output **must** be a **list** of `Planet` objects.\n",
+    "\n",
+    "**Hint:** If you did not get the right answer here, it is possible that you did not read the files in the correct **order**. In `planets_list`, the planets from `planets_1.csv` should appear first (in the order that they appear in the dataset), followed by the planets from `planets_2.csv`, `planets_3.csv`, `planets_4.csv`, and `planets_5.csv`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "17012db1",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'planets_4520_4525', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "d5a637fd",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q16\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "1f7d9e45",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 17:** How many planets in `planets_list` were discovered in the year *2022*?\n",
+    "\n",
+    "Your output **must** be an **integer**."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "744d1e21",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'planets_disc_2022', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "ad0634ca",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q17\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6fbe2141",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 18**: Find the `Star` object around which the `Planet` named *TOI-2202 c* orbits.\n",
+    "\n",
+    "Your output **must** be a `Star` object.\n",
+    "\n",
+    "**Hint:** You **must** first find the `Planet` object with the `planet_name` *TOI-2202 c* and then use the `host_name` attribute to identify the name of the star around which the planet orbits. Then, you can get the `Star` object using the `stars_dict` **dictionary** defined above.\n",
+    "\n",
+    "You **must** exit the loop once you find the first planet with the target name."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9fd23c1e",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'toi_2022_c_star', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "343f6c12",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q18\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3228985b",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 19:** Find the **average** `planet_radius` (in units of the radius of the Earth) of the planets that orbit stars with `stellar_radius` more than *10* (i.e. more than *10* times the radius of the Sun).\n",
+    "\n",
+    "Your output **must** be a **float**. You **must** skip any `Planet` objects with **missing** `planet_radius` data and any `Star` objects with **missing** `stellar_radius` data."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "a4f3a348",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'avg_planet_radius_big_stars', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "8402220f",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q19\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "eabf4315",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "**Question 20**: Find all the `Planet` objects that orbit the **youngest** `Star` object.\n",
+    "\n",
+    "Your output **must** be a **list** of `Planet` objects (even if there is **only one** `Planet` in the list). The age of a `Star` can be found from its `stellar_age` column. You do **not** have to worry about any ties. There is a **unique** `Star` in the dataset which is the youngest star."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "60db3bcc",
+   "metadata": {
+    "lines_to_next_cell": 0,
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "# compute and store the answer in the variable 'youngest_star_planets', then display it\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "80bd5bf1",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.check(\"q20\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "f9312153",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    "## Submission\n",
+    "Make sure you have run all cells in your notebook in order before running the following cells, so that all images/graphs appear in the output. The following cells will generate a zip file for you to submit.\n",
+    "\n",
+    "**SUBMISSION INSTRUCTIONS**:\n",
+    "1. **Upload** the zipfile to Gradescope.\n",
+    "2. Check **Gradescope otter** results as soon as the auto-grader execution gets completed. Don't worry about the score showing up as -/100.0. You only need to check that the test cases passed."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f81183d4",
+   "metadata": {
+    "cell_type": "code",
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "from IPython.display import display, Javascript\n",
+    "display(Javascript('IPython.notebook.save_checkpoint();'))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "603cb1d0",
+   "metadata": {
+    "cell_type": "code",
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "!jupytext --to py p10.ipynb"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b713ce0a",
+   "metadata": {
+    "cell_type": "code",
+    "deletable": false,
+    "editable": false
+   },
+   "outputs": [],
+   "source": [
+    "grader.export(pdf=False, run_tests=True, files=[py_filename])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "280d3a82",
+   "metadata": {
+    "deletable": false,
+    "editable": false
+   },
+   "source": [
+    " "
+   ]
+  }
+ ],
+ "metadata": {
+  "jupytext": {
+   "cell_metadata_filter": "-all",
+   "encoding": "# coding: utf-8",
+   "executable": "/usr/bin/env python",
+   "notebook_metadata_filter": "-all"
+  },
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.13"
+  },
+  "otter": {
+   "OK_FORMAT": true,
+   "tests": {
+    "planet_object": {
+     "name": "planet_object",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"qplanet_object\", jupiter)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q1": {
+     "name": "q1",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q1\", files_in_data)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q10": {
+     "name": "q10",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q10\", biggest_star)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q11": {
+     "name": "q11",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q11\", avg_age_kepler)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q12": {
+     "name": "q12",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q12\", fifth_planet)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q13": {
+     "name": "q13",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q13\", last_five_planets_1)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q14": {
+     "name": "q14",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q14\", controversial_planets)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q15": {
+     "name": "q15",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q15\", last_five_planets_4)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q16": {
+     "name": "q16",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q16\", planets_4520_4525)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q17": {
+     "name": "q17",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q17\", planets_disc_2022)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q18": {
+     "name": "q18",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q18\", toi_2022_c_star)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q19": {
+     "name": "q19",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q19\", avg_planet_radius_big_stars)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q2": {
+     "name": "q2",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q2\", file_paths)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q20": {
+     "name": "q20",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q20\", youngest_star_planets)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q3": {
+     "name": "q3",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q3\", csv_file_paths)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q4": {
+     "name": "q4",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q4\", stars_paths)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q5": {
+     "name": "q5",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q5\", third_star)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q6": {
+     "name": "q6",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q6\", dp_leo)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q7": {
+     "name": "q7",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q7\", avg_lum_stars_1)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q8": {
+     "name": "q8",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q8\", avg_age_stars_2)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "q9": {
+     "name": "q9",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"q9\", kepler_220_temp)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    },
+    "star_object": {
+     "name": "star_object",
+     "points": 0,
+     "suites": [
+      {
+       "cases": [
+        {
+         "code": ">>> p10_test.check(\"qstar_object\", sun)\nTrue",
+         "hidden": false,
+         "locked": false
+        }
+       ],
+       "scored": true,
+       "setup": "",
+       "teardown": "",
+       "type": "doctest"
+      }
+     ]
+    }
+   }
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/sum23/projects/p10/p10_test.py b/sum23/projects/p10/p10_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..49d07f69d2b470058b312dc1ffda09da4a87951d
--- /dev/null
+++ b/sum23/projects/p10/p10_test.py
@@ -0,0 +1,378 @@
+#!/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
diff --git a/sum23/projects/p11/rubric.md b/sum23/projects/p11/rubric.md
index 408dc3ffb27436a03223d21ad5132bba07618b41..c8a41deda64d9f2e5df48778db27f1bcea7ce6d0 100644
--- a/sum23/projects/p11/rubric.md
+++ b/sum23/projects/p11/rubric.md
@@ -68,7 +68,7 @@
 	- Required data structure is not used (-1)
 	- Incorrect logic is used to answer (-1)
 	- Plot is not properly labeled (-1)
-	- Plot is incorrect (-1)ible/not properly labeled (-2)
+	- Plot is incorrect (-1)
 
 - `get_all_paths_in` (6)
 	- Hardcoding the name of directory inside the function (-1)