In this MP you will both build and use web service APIs. In particular,
/weather
and one request body field, course
/weatherCache
The code comes with a simple web front-end that lets you interact with your completed service through your web browser.
Initial files are available in mp8.zip, including one file you’ll edit:
classweather.py
We have fully implemented a course meeting time microservice for you. You can find it in the courses_microservice
directory. This, like the microservice you will implement, uses aiohttp to automate many of the tedious parts of running a webserver.
cd
into the courses_microservice
directory and run it as python3 schedules.py
. It should print out something like
======== Running on http://0.0.0.0:34000 ========
(Press CTRL+C to quit)
and then wait for you to use it.
On a GET
request to the path /<subject>/<number>/
, the microservice will respond with a JSON object with three keys: course
, Start Time
and Days of Week
. For example, the following is the result of a request to /CS/340/
:
{
"course": "CS 340",
"Days of Week": "TR",
"Start Time": "02:00 PM"
}
If the microservice is unable to find the course, it will return a 404
and JSON with an error
key:
{
"course": "CS 300",
"error": "CS 300 not found in schedule"
}
Assuming you’ve run this on your own machine, you can open a browser and try this out by visiting
If you ran it on your VM, use your VM name (e.g.
) instead of fa24-cs340-987.cs.illinois.edu
.localhost
You can also use it with aiohttp’s web client tools, which is what the microservice you implement will do.
Complete classweather.py
such that
POST /weather
will respond with the weather forecast for the next meeting of the class provided in the request.
The course will be supplied in a JSON object, inside object key "course"
. It may be in several forms (CS 340
, cs340
, or other such combinations). You may assume it is an all-alphabetic course mnemonic followed by a 3-digit course number.
Meeting times are found from the course microservice.
You must get the URL of the microservice from the system environment using the following code
= os.getenv('COURSES_MICROSERVICE_URL') server_url
This will give a URL with no path, like http://localhost:34000
(no trailing slash). We may change the URL during testing to ensure you are using the microservice correctly.
The next meeting time is always in the future. If a course meets Tuesday/Thursday 2–3:15pm then on Thursday at 2:01pm the next meeting time is next-week Tuesday at 2 (5 days in the future), not this-week Thursday at 2 (1 minute in the past).
Weather forecasts are found from the National Weather Service API.
Use (40.11,-88.24) as the GPS coordinates for all locations.
Use the forecast for the specific time that a course begins, rounded down to an integer hour. Do not account for holidays, end of semester, or the like.
If you submit too many requests in a short time, the National Weather Service will see your computer as operating maliciously and will block future requests for several minutes or hours.
Because of this, use your application sparingly until after you implement caching (see below).
Cache weather forecasts; if we request the same course again, don’t contact the National Weather Service API for the second request, only the first.
You must report the data back in a response JSON with the following structure with a status 200 response:
{
"course": "CS 340",
"nextCourseMeeting": "2022-10-25 02:00:00",
"forecastTime": "2022-10-25 02:00:00",
"temperature": 60,
"shortForecast": "Mostly Sunny"
}
The national weather service provides slightly less than a full week of hourly forecasts. If the next course meeting time is after the last forecast they have, respond with both "temperature"
and "shortForecast"
as "forecast unavailable"
:
{
"course": "CS 340",
"nextCourseMeeting": "2022-10-19 02:00:00",
"forecastTime": "2022-10-19 02:00:00",
"temperature": "forecast unavailable",
"shortForecast": "forecast unavailable"
}
On any other sort of error (ex: when the course cannot be found), respond with a status 400 response and a JSON object containing the key "error"
and a message describing what went wrong.
GET /weatherCache
must return the current state of the cache as a JSON object. This must be an empty value ([]
, {}
, or ""
) before any requests are made, and after at least one request is made it must include the forecasts for any course queried so far.
return json_response(data=your_cache)
is the recommended way to achieve this.
Your program must make HTTP requests to the schedule microservice. The aiohttp client quickstart shows an example of doing this. Note the two nested async with
statements: you do need both, nested in that order, and can’t return the resp
directly. Also note that the resp
is a ClientResponse and has other methods you might find helpful as well.
When calling the course schedules microservice, you need to use HTTP, not HTTPS.
When calling the National Weather Service, you need to use HTTPS. This both uses certificates so you know you are contacting the correct server and encryption so you know your messages are not being read or tampered with along the way.
Some students have reported errors connecting to these pages based on a mismatch between their installed SSL certificates and those used by one or both of these servers.
You might be able to fix this by installing certifi
as either sudo apt install python3-certifi
or python3 -mpip install certifi
.
You can also bypass certificate checking entirely. This is dangerous in general, meaning you might be talking to a different server than you expected, but as long as the messages you send contain no sensitive information and having someone spoof the messages wouldn’t cause any harm, you can do it by changing your ClientSession constructor to be
=aiohttp.TCPConnector(ssl=False)) # INSECURE! aiohttp.ClientSession(connector
You can find the current date and time usingdatetime.now()
.
You can construct a new datetime either by adding a timedelta
to an existing datetime or by supplying the year, month, day, hour, and so on to the constructor.
There are quite powerful string-to-date and date-to-string functions in the datetime library, but they’re complicated enough to be tricky to use properly. It might be easier to handle those tasks manually.
You don’t need to be super efficient. Even inefficient code is likely to finish in a fraction of the time needed to contact the weather service API.
If I ask your app for the weather for a set of courses, and then ask again for the same set of courses, it must not access the National Weather Service for the second set of requests. But if I shut your app off, turn it on again, and ask for that same set of courses again, it should access the National Weather Service that time.
The easiest way to handle this is to store results in a global variable; something like
= {}
whatever_you_name_your_cache
def my_cached_function(args):
global whatever_you_name_your_cache
if args in whatever_you_name_your_cache:
return whatever_you_name_your_cache[args]
= compute_from(args)
result = result
whatever_you_name_your_cache[args] return result
There are many variations on that general pattern.
You are welcome to decide how to handle your cache. Last semester we saw working solutions along all of the following patterns:
You’re welcome to also cache course ↦ meeting time, but doing so is not required or expected.
A frontend has been provided for you to test your web application.
To run this front-end, you need to run the courses microservice and your app and give your app the URL of the microservice in an environment variable. We’ve provided a simple make start
script to do this for you. Once you make start
you can connect to http://localhost:5000/ (possibly replacing localhost
with the full name of your VM) to view your app.
make start
does
There are two lines in the make start
target:
python3 courses_microservice/schedules.py &
COURSES_MICROSERVICE_URL=http://localhost:34000 python3 classweather.py
python3 courses_microservice/schedules.py
runs the courses microservice&
puts the courses microservice in the background, allowing another command to run before it completesCOURSES_MICROSERVICE_URL=http://localhost:34000
sets an environment variable for the command run on that linepython3 classweather.py
runs your appEnsure that your server works for courses many days in the future AND for courses later the same day. (Ex: If you check your service at 9:00am for your 1:00pm class, it should report weather back for the 1:00pm class that same day.)
Test several different courses:
pytest
We provide automated tests, which you can run with make test
or python3 -m pytest
.
These tests use a checking service on fa24-cs340-adm.cs.illinois.edu to verify your data. That is set up to run continuously, but if it goes down for some reason the automated tests won’t work until it is restarted. Automated restart attempts are made every 5 minutes; if those fail then the service won’t be able to restart until business hours when the professor and/or IT staff can fix it.
The National Weather Service updates their forecasts roughly once an hour, but does not publish when those updates will occur. There’s a very small chance (well under 0.1%) that any given run will have your code and the checker code get different forecasts; if that happens, wait a few seconds for the updates to complete and then try again.