Mini Project 2: A tool on live data
Due Sunday, 10/25, 11:59 PM
On this page
Build a tool that answers a question from data it fetches when it runs. The data comes from an API, through a function you wrote. Choose one option below: A, B, C or D. D is a tool of your own, about as much work as the others, and needs no approval.
How to start#
- Make the notebook. In the VS Code Explorer, click the empty space below the list of files, so that nothing is selected, then click New Folder and type
projects/mp2. Theprojectsfolder should sit next to thenotebooksfolder, not inside it. Then, in a Terminal opened in the root folder of your repository, runuv run marimo edit projects/mp2/<your-tool>.py, with a name for what your tool does, such astown-tax-finder.py, and marimo creates the notebook. Give it the sections under "Your Notebook" below as its headings. - Fill in section 1, the question, and section 2, your plan, before you write any code or ask your agent anything, and commit only those two sections, with the message
mp2: plan before AI. - Open the API's address in your browser first and read what comes back, then work with your agent. When it writes a line you could not have written yourself, ask it to explain that line before you keep it. Commit as you go.
| Role | Where it applies |
|---|---|
| By hand | Your plan, reading the API's reply before any code, and section 8 |
| AI writes, you check | Any code your agent writes, read and tested before you keep it |
| You direct, you verify | The Going further part |
| You choose the question | Your own option, if you choose D |
A · Where the Tax Bill Is Lowest#
A couple is looking for a single-family home in Middlesex County with a budget of $700,000, and they want to keep the property tax bill under $8,000 a year. They want a short list of towns where the average home fits the budget and the average bill fits the limit, cheapest bill first, and they want to rerun it for another county or another budget.
The data is the course API's Massachusetts endpoints, built from the state's Division of Local Services: oim.zhili.dev/ma lists them, and oim.zhili.dev/docs describes every field.
county = "Middlesex"
budget_dollars = 700000
tax_limit_dollars = 8000
fiscal_year = 2026The main task
- Every town in the county, from
/ma/towns?county=.... The reply'spaginationreports how many towns there are; make sure you have all of them. - For each town, its figures for
fiscal_yearfrom/ma/towns/{dor_code}/history. Compare every town for the same year. - A table of the towns that pass both limits, cheapest bill first: town, average home value, residential tax rate, and average bill.
- One sentence that answers the couple, with the numbers in it.
You may need the params= argument of requests.get. A page holds 50 towns unless you ask for more, up to 100.
Going further
- Rank the same towns by tax rate in place of the bill. Does the order change, and why?
- Show how the bill has changed over 10 years for the three towns with the lowest bill on your list.
- Add the town's income per person and state the bill as a share of it.
- Let the couple change the county and the limits without editing code.
B · The Shop's Week#
Northwind Traders sells specialty food to shops and restaurants, and its orders arrive through the course API every day. The operations manager wants a report every Monday on the last seven days: how many orders came in, how much they were worth, where they went, what sold most, and which orders have not shipped yet.
The data is the course API's shop endpoints: oim.zhili.dev/shop lists them, and oim.zhili.dev/docs describes every field. New orders appear every day, so a classmate who runs it on another day will get other numbers.
days = 7The main task
- Every order placed in the
daysdays that end today, today included: withdays = 7, today and the six days before it. Ask/shop/orderswithfrom_dateandto_date. The reply'spaginationreports how many orders there are; make sure you have all of them. - Each order's lines from
/shop/orders/{order_id}. - One report: the number of orders, the revenue (the sum of the order totals, which leave out freight), revenue by country, the five products that sold the most units, and the orders not shipped yet, which are those whose
shipped_dateis empty. - One sentence for the manager, with the numbers in it.
You may need date.today() and timedelta(days=...) from datetime to find the first day, and a dictionary that counts. from_date and to_date take dates written as 2026-10-04, which is how str() writes a date.
Going further
- Compare the week with the same seven days a year earlier.
- Find the customers who ordered this week and not in the four weeks before.
- Show revenue by day as a chart.
- Run the report automatically every Monday with GitHub Actions.
C · Pick the Day#
A student club is planning an outdoor event somewhere in Massachusetts in the next week and needs to pick the day. The organizer wants a table of the next seven days, today included, whether each day meets the club's rules, and the best day, for whatever town they type in.
The data is Open-Meteo, which needs no key: its geocoding search turns a place name into coordinates, and its forecast gives seven days ahead.
place = "Wellesley"
state = "Massachusetts"
lowest_high_f = 55
highest_high_f = 80
max_rain_chance = 30
max_wind_mph = 15The main task
- The coordinates of
placefrom the geocoding search. A name can match places in several states, and several places in one state: use the first match instate, which is the largest, and name it in section 5. - The daily forecast for those coordinates, in °F and mph: the high, the low, the chance of rain and the strongest wind for each day. The reply gives each of them as its own list; turn them into one record per day.
- A table with one row per day: the date, the high, the low, the chance of rain, the strongest wind, and whether the day passes every rule. A value equal to a limit passes.
- One sentence naming the best day, with the reason. When several days pass, the best has the lowest chance of rain, and on a tie the lower wind. If no day passes, name the day that breaks the fewest rules, and the earlier one on a tie.
You may need the search's count= parameter, up to 100, since the search returns 10 places by default and Lexington, Massachusetts comes 13th. The forecast takes the daily= parameter, with temperature_unit=fahrenheit, wind_speed_unit=mph and timezone=America/New_York, so that each day runs from midnight to midnight in Boston, and zip() walks several lists at once.
Going further
- Compare two or three towns and pick the best place as well as the best day.
- Ask a second forecast service and compare the two.
- Score each day out of 10 in place of pass and fail.
- Let the organizer change the rules without editing code.
D · Your Own Option#
Build a tool of your own on any public API, in a field you know, about as much work as A, B or C. Its main task needs:
- Its inputs together in one cell at the top
- More than one request, where the answer to one tells you what to ask next
- A table of results and a sentence that answers the question
An API that needs a key is allowed here; the rules are below. Choose your own Going further, and describe it in section 9.
Whichever You Choose#
- Your own function fetches the data. Write it with
def: it takes what it needs as parameters, makes the requests withrequests, and returns the data as a list of records. Everything after it is plain Python: lists, dictionaries, loops, conditions and f-strings. When a step goes through a list, write it as aforloop; if your agent gives you a one-line version, ask it for the loop. Use no other library in the main task: Python's own modules, such asdatetime, are fine, and so is python-dotenv for an API that needs a key.requestsis already in your project. - The data is fetched when the notebook runs. Nothing in the main task reads a file, except a saved copy of the data for an API that needs a key (below).
- Save what you fetched. Save the records your function returned to a CSV or JSON file in
projects/mp2/data/, and commit it. - Your inputs sit together at the top, so that changing one changes every result after it.
- A name can be defined in only one cell, so start a loop's names with an underscore, as in notebook 3:
for _order in orders:.
Your Notebook#
Use these as its headings, in this order.
The Question. Who would use this, and what decision does it help them make? Two or three sentences, in words somebody outside this course would understand.
My Plan Before AI. Before you ask your agent anything, open the API's address in your browser and read what comes back. Then write how you would solve it: the steps, in order, in plain words, in five lines or more. Then answer these:
- Which functions will your tool need? For each, what goes in and what comes out?
- Which addresses will they request?
- How will you know you have every record?
- Which check will you use in section 6, and which two numbers should agree?
Commit with the message
mp2: plan before AI.Inputs. Every input in one cell.
The Work. Your functions, and the code that uses them.
The Answer. Your result table, printed with
printand f-strings with the columns lined up, and one sentence that answers the question in section 1 with the numbers in it. Print the sentence from code with an f-string, so that it changes when the data does.How I Know These Numbers Are Right. Reach at least one result a second way, in this notebook, and show that the two agree. Write a cell that gets the same number from other fields, or open the raw reply in your browser and find each number of one row of your table there. For instance: an average bill is close to the average value times the tax rate divided by 1,000; an order's total equals the sum of its lines; the number of records you collected equals the total the API reports. At least one of those checks runs as code, and shows both numbers.
Working With the Agent. Pick one piece of AI output you did not accept as-is. What did it give you, what did you change, and how did you know? Point to the commit or the cell. If the agent got it right the first time: What did you do to verify that? Pointing to your check in section 6 is enough.
Reading It Back. Pick two functions in your notebook that your agent wrote or helped write. For each, write in your own words what goes in, what comes out, and one line you had to ask about. Then choose one change to what the tool does, such as another county, another week or another rule. Changing one value in your Inputs cell is enough. Write what you expect to change in the answer. Commit with the message
mp2: prediction, make the change, run it, and write what happened and whether it matched.Going Further. What you tried past the main task, what you found, and where it is in the notebook.
Time Spent. About how many hours this project took you outside class, as one number. It is not graded.
An API That Needs a Key#
- Before you make the key file, open
.gitignorein the root folder of your repository and check that it lists.env. If the file is missing or does not list it, add the line.envand commit that first. Your repository is public, and a key committed once stays in its history. - Make the key file in VS Code, in the root folder of your repository, named exactly
.env. Notepad and TextEdit can add an extension to the name, and.env.txtis not ignored. - Before every commit, look at the list of changed files in GitHub Desktop. An ignored file never appears there, so if
.envis in the list, it is not ignored: do not commit. - Read the key with python-dotenv (
uv add python-dotenv). Never type the key into the notebook. - Make the notebook read the copy you saved in
projects/mp2/data/when no key is found, so that somebody without your key can still run it. - Never submit your key or send it to anybody, including me.
Going Further#
- It is required, and any direction counts. Take at least one step past the main task, either one of the ideas listed under your option or one of your own. It does not have to work. Be ambitious here, and use your agent as much as you like. Describe what you tried in section 9.
- Any library is allowed, polars and charts included: add it with
uv addso it is recorded inpyproject.toml, and import it only in a Going further cell. - Directions any option can take, besides the ideas under your option:
- Add a second API. Bring in data from a second API and combine it with the first in whatever way makes your answer better or more interesting.
- Let somebody use it without editing code. A marimo notebook also runs as a script,
uv run python projects/mp2/<your-tool>.py. Ask for the inputs withinput(), take them from the command line, or give them controls and open the notebook withuv run marimo run. - Put a model inside your tool. Write a function that sends the numbers your tool found to a model, with your Babson key, and returns the paragraph the model writes for your user. A paragraph you get from the chat panel or from your agent does not count. This one comes from your code each time the tool runs, so it changes when the data does.
- Run it on a schedule. GitHub Actions can rerun the notebook every day or every week, with any key stored as a repository secret. Keep what each run found, and use that history to answer something a single run cannot.
Submit#
The link to your notebook on GitHub, in this form:
https://github.com/<your-github-username>/<course-code>/blob/main/projects/mp2/<your-tool>.pyThe link has to point into projects/mp2/ in your course repository. If your notebook is anywhere else, move it there before you copy the link.
Grading#
| What I look at | Points |
|---|---|
| The main task runs, and its results are right | 3 |
| Section 6 reaches one result a second way, the two agree, and at least one check runs as code | 2 |
| Section 8 explains two functions, and the prediction was committed before the change | 2 |
| Your plan was committed before any code from your agent, and section 7 points to a commit or a cell | 1 |
| Section 9 describes a Going further you tried | 1 |
| The question and your answer are clear to somebody outside this course | 1 |
The table above is a guide. I read each notebook as a whole.
Last updated: Tuesday, 10/20/2026