diff --git a/.gitignore b/.gitignore index ffbf390..c0a2f68 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,4 @@ target/ .DS_Store .idea .env +venv diff --git a/apps/05_weather_client/final/example_dict.py b/apps/05_weather_client/final/example_dict.py new file mode 100644 index 0000000..914dd78 --- /dev/null +++ b/apps/05_weather_client/final/example_dict.py @@ -0,0 +1,46 @@ +def main(): + d = { + 'city': 'Portland', + 'state': 'ME', + 'details': ['Cold', 'Snowy', 'Winter'] + } + + print(d.get('country', 'USA')) + d['country'] = 'AU' + print(d.get('country', 'USA')) + print(d) + + w = { + "weather": { + "description": "scattered clouds", + "category": "Clouds" + }, + "wind": { + "speed": 2.42, + "deg": 221 + }, + "units": "imperial", + "forecast": { + "temp": 64.08, + "feels_like": 63.86, + "pressure": 1023, + "humidity": 69, + "low": 60.01, + "high": 68 + }, + "location": { + "city": "Portland", + "country": "US", + "state": "OR" + }, + "rate_limiting": { + "unique_lookups_remaining": 49, + "lookup_reset_window": "1 hour" + } + } + + print(w.get('forecast').get('temp')) + + +if __name__ == '__main__': + main() diff --git a/apps/05_weather_client/final/program.py b/apps/05_weather_client/final/program.py index 6600c75..db5e227 100644 --- a/apps/05_weather_client/final/program.py +++ b/apps/05_weather_client/final/program.py @@ -1,88 +1,114 @@ -import requests -import bs4 import collections +import requests -WeatherReport = collections.namedtuple('WeatherReport', - 'cond, temp, scale, loc') +Location = collections.namedtuple('Location', 'city state country') +Weather = collections.namedtuple('Weather', 'location units temp condition') def main(): - # t = 1, 7, 'cat', [1,2,3] - # print(t) - # print(t[1]) - # - # n1, n2, s, l = t - # print(n1, n2, s, l) - # return + show_header() + location_text = input("Where do you want the weather report (e.g. Portland, US)? ") + loc = convert_plaintext_location(location_text) + if not loc: + print(f"Could not find anything about {location_text}.") + return - print_the_header() + weather = call_weather_api(loc) + if not weather: + print(f"Could not get weather for {location_text} from the API.") + return - code = input('What zipcode do you want the weather for (97201)? ') + report_weather(loc, weather) - html = get_html_from_web(code) - report = get_weather_from_html(html) - print('The temp in {} is {} {} and {}'.format( - report.loc, - report.temp, - report.scale, - report.cond - )) +def report_weather(loc, weather): + location_name = get_location_name(loc) + scale = get_scale(weather) + print(f'The weather in {location_name} is {weather.temp} {scale} and {weather.condition}.') - # display for the forecast +def get_scale(weather): + if weather.units == 'imperial': + scale = "F" + else: + scale = "C" + return scale + + +def get_location_name(location): + if not location.state: + return f'{location.city.capitalize()}, {location.country.upper()}' + else: + return f'{location.city.capitalize()}, {location.state.upper()}, {location.country.upper()}' -def print_the_header(): - print('---------------------------------') - print(' WEATHER APP') - print('---------------------------------') - print() +def call_weather_api(loc): + # &state=OR + url = f'https://weather.talkpython.fm/api/weather?city={loc.city}&country={loc.country}&units=imperial' + if loc.state: + url += f"&state={loc.state}" -def get_html_from_web(zipcode): - url = 'http://www.wunderground.com/weather-forecast/{}'.format(zipcode) - response = requests.get(url) - # print(response.status_code) - # print(response.text[0:250]) + # print(f"Would call {url}") + resp = requests.get(url) + if resp.status_code in {400, 404, 500}: + # print(f"Error: {resp.text}.") + return None - return response.text + data = resp.json() + return convert_api_to_weather(data, loc) -def get_weather_from_html(html): - # cityCss = '.region-content-header h1' - # weatherScaleCss = '.wu-unit-temperature .wu-label' - # weatherTempCss = '.wu-unit-temperature .wu-value' - # weatherConditionCss = '.condition-icon' - soup = bs4.BeautifulSoup(html, 'html.parser') - loc = soup.find(class_='region-content-header').find('h1').get_text() - condition = soup.find(class_='condition-icon').get_text() - temp = soup.find(class_='wu-unit-temperature').find(class_='wu-value').get_text() - scale = soup.find(class_='wu-unit-temperature').find(class_='wu-label').get_text() +def convert_api_to_weather(data, loc): + # 'weather': {'description': 'broken clouds', 'category': 'Clouds'} + # 'forecast': {'temp': 66.34, - loc = cleanup_text(loc) - loc = find_city_and_state_from_location(loc) - condition = cleanup_text(condition) - temp = cleanup_text(temp) - scale = cleanup_text(scale) + temp = data.get('forecast').get('temp') + w = data.get('weather') + condition = f"{w.get('category')}: {w.get('description').capitalize()}" + weather = Weather(loc, data.get('units'), temp, condition) - # print(condition, temp, scale, loc) - # return condition, temp, scale, loc - report = WeatherReport(cond=condition, temp=temp, scale=scale, loc=loc) - return report + return weather -def find_city_and_state_from_location(loc: str): - parts = loc.split('\n') - return parts[0].strip() +def convert_plaintext_location(location_text): + if not location_text or not location_text.strip(): + return None + location_text = location_text.lower().strip() + parts = location_text.split(',') -def cleanup_text(text: str): - if not text: - return text + city = "" + state = "" + country = 'us' + if len(parts) == 1: + city = parts[0].strip() + elif len(parts) == 2: + city = parts[0].strip() + country = parts[1].strip() + elif len(parts) == 3: + city = parts[0].strip() + state = parts[1].strip() + country = parts[2].strip() + else: + return None - text = text.strip() - return text + # print(f"City={city}, State={state}, Country={country}") + + # t = city, state, country + # t[0] + # t2 = Location(city, state, country) + # t2.city + + return Location(city, state, country) + # return city, state, country + + +def show_header(): + print('---------------------------------') + print(' WEATHER CLIENT') + print('---------------------------------') + print() if __name__ == '__main__': diff --git a/apps/05_weather_client/you_try/app-5-screenshot.png b/apps/05_weather_client/you_try/app-5-screenshot.png index fe25e7a..79cc785 100644 Binary files a/apps/05_weather_client/you_try/app-5-screenshot.png and b/apps/05_weather_client/you_try/app-5-screenshot.png differ diff --git a/apps/06_lolcat_factory/you_try/README.md b/apps/06_lolcat_factory/you_try/README.md index 5320182..fccb908 100644 --- a/apps/06_lolcat_factory/you_try/README.md +++ b/apps/06_lolcat_factory/you_try/README.md @@ -32,4 +32,6 @@ Key concepts introduced subprocess.call(['explorer', folder]) # Linux - subprocess.call(['xdg-open', folder]) \ No newline at end of file + subprocess.call(['xdg-open', folder]) + +Note: On some Linux distributions, xdg-open may not be available. If that's the case for you, please look at [this article](https://budts.be/weblog/2011/07/xdf-open-vs-exo-open/). diff --git a/apps/07_wizard_battle/final/program.py b/apps/07_wizard_battle/final/program.py index 64d04d2..229b139 100644 --- a/apps/07_wizard_battle/final/program.py +++ b/apps/07_wizard_battle/final/program.py @@ -10,11 +10,11 @@ def main(): def print_header(): - # Yes, I added this after I recorded the video + # Yes, I added this after I recorded the video, # but I thought you'd get a kick out if it. ;) print() print('-----------------------------------------------------------------------') - print(''' + print(r''' ( ) /\ _ ( \ | ( \ ( \.( ) _____ \ \ \ ` ` ) \ ( ___ / _ \\ @@ -50,7 +50,7 @@ def game_loop(): while True: active_creature = random.choice(creatures) - print('A {} of level {} has appear from a dark and foggy forest...' + print('A {} of level {} has appeared from a dark and foggy forest...' .format(active_creature.name, active_creature.level)) print() diff --git a/apps/08_file_searcher/final/program.py b/apps/08_file_searcher/final/program.py index 40889fa..0503c5d 100644 --- a/apps/08_file_searcher/final/program.py +++ b/apps/08_file_searcher/final/program.py @@ -77,18 +77,27 @@ def search_folders(folder, text): def search_file(filename, search_text): - # matches = [] - with open(filename, 'r', encoding='utf-8') as fin: - - line_num = 0 - for line in fin: - line_num += 1 - if line.lower().find(search_text) >= 0: - m = SearchResult(line=line_num, file=filename, text=line) - # matches.append(m) - yield m - - # return matches + + # NOTE: We haven't discussed error handling yet, but we + # cover it shortly. However, some folks have been running + # into errors where this is passed a binary file and crashes. + # This try/except block catches the error and returns no matches. + try: + + # matches = [] + with open(filename, 'r', encoding='utf-8') as fin: + + line_num = 0 + for line in fin: + line_num += 1 + if line.lower().find(search_text) >= 0: + m = SearchResult(line=line_num, file=filename, text=line) + # matches.append(m) + yield m + + # return matches + except UnicodeDecodeError: + print("NOTICE: Binary file {} skipped.".format(filename)) if __name__ == '__main__': diff --git a/apps/10_movie_search/final/movie_svc.py b/apps/10_movie_search/final/movie_svc.py index 06f90b1..cc49e4c 100644 --- a/apps/10_movie_search/final/movie_svc.py +++ b/apps/10_movie_search/final/movie_svc.py @@ -11,7 +11,8 @@ def find_movies(search_text): if not search_text or not search_text.strip(): raise ValueError("Search text is required") - url = 'http://movie_service.talkpython.fm/api/search/{}'.format(search_text) + # This URL changed since the recording to support SSL. + url = 'https://movieservice.talkpython.fm/api/search/{}'.format(search_text) resp = requests.get(url) resp.raise_for_status() diff --git a/apps/10_movie_search/you_try/README.md b/apps/10_movie_search/you_try/README.md index 7a9ef45..2588b37 100644 --- a/apps/10_movie_search/you_try/README.md +++ b/apps/10_movie_search/you_try/README.md @@ -13,7 +13,7 @@ Key concepts introduced **The API** -You can find the details of the JSON HTTP API at [movie_service.talkpython.fm](http://movie_service.talkpython.fm/). +You can find the details of the JSON HTTP API at [movieservice.talkpython.fm](https://movieservice.talkpython.fm/). **Try/Except Error Handling** diff --git a/readme_resources/video_play.png b/readme_resources/video_play.png index 8936bce..f607451 100644 Binary files a/readme_resources/video_play.png and b/readme_resources/video_play.png differ diff --git a/transcripts/txt/05_app/01_intro.txt b/transcripts/txt/05_app/01_intro.txt new file mode 100644 index 0000000..cd933ba --- /dev/null +++ b/transcripts/txt/05_app/01_intro.txt @@ -0,0 +1,22 @@ +00:0.14 Hello and welcome to app number five. +00:2.54 This time we're gonna build a weather client; an application that you run, and you can +00:6.64 ask "what is the weather" in virtually any town in the world. +00:10.94 Now, this is not a simulated weather client. +00:13.57 It's not going to just give some random thing like "maybe it's gonna be cloudy and +00:18.01 72". No, it's going to go out to the Internet and actually get the +00:21.69 weather at that location. So we're going to do some really, +00:24.92 really cool stuff. Let's see exactly what we're going to build. +00:29.84 It's gonna look like this. It'll have a little header like all of our apps +00:33.21 do, and it will say +00:34.55 "enter your location" and you put in a city and a country, or just a city +00:39.03 within the U.S., or a city and a state in the country +00:42.71 U.S. However you want. Here, +00:44.23 we're gonna say "I'd like to know the weather in Seattle" and right now, when +00:48.12 I ran this, it said the weather in Seattle is 54.48 degrees Fahrenheit and the +00:53.1 forecast is clouds, specifically scattered clouds. +00:56.54 And then I asked for Stuttgart, Germany, Deutschland, +00:59.84 the weather in Stuttgart, Germany +01:1.45 is 47°F and overcast clouds. +01:4.72 Cool, right? So this is what we're gonna build and you'll see, +01:7.55 While it may sound complicated to go out actually to the Internet and get all this +01:11.44 information with Python, it's gonna be awesome. diff --git a/transcripts/txt/05_app/02_new_chapter_version.txt b/transcripts/txt/05_app/02_new_chapter_version.txt new file mode 100644 index 0000000..78daf24 --- /dev/null +++ b/transcripts/txt/05_app/02_new_chapter_version.txt @@ -0,0 +1,24 @@ +00:0.04 If this is the first time you're taking this course, +00:1.66 you could basically disregard the next two screens here. +00:4.14 But if you've already taken this course and gone through this chapter, +00:7.36 you should know that we've completely redone it. +00:10.29 We've re-created it from scratch. +00:12.68 Why? Well, we use this awesome place "wunderground.com" +00:16.8 and we would go there and we'd get the weather from "wunderground.com". +00:19.74 However, it's been a couple of years, +00:21.7 and Wunderground has dramatically changed their website. +00:25.1 So the presentation that we were giving was not really something you could re-create. +00:29.99 So, what have we done? Well, +00:31.6 I've built a new weather service, +00:34.5 and I'm hosting it over at Talk Python. +00:36.76 So, "weather.talkpython.fm" +00:39.14 we're going to use this weather API to get real live weather data for +00:44.16 our application, Okay? So if you've seen this course before and you've already gone +00:48.04 through it and you're wondering "hey, why did all these videos change?" +00:50.78 this is why. It's just to refresh and keeping things modern. +00:54.24 If you want to get back to the old code, +00:56.12 go to the GitHub repository for the course handouts, +00:59.41 the course demos, and just switch branches to "Old Weather APP chapter" and you'll see that +01:3.55 code. Otherwise, you're also going to see the latest code on the GitHub +01:7.61 repository. With that out of the way, +01:9.83 let's go build our awesome weather app against our brand new shiny weather API. diff --git a/transcripts/txt/05_app/03-what-you%20will-learn.txt b/transcripts/txt/05_app/03-what-you%20will-learn.txt new file mode 100644 index 0000000..b70ffcc --- /dev/null +++ b/transcripts/txt/05_app/03-what-you%20will-learn.txt @@ -0,0 +1,44 @@ +00:0.04 What are we gonna learn in this chapter? +00:1.67 Well, a ton of amazing things. +00:4.94 The primary focus, the thing that we're going to get the most out of with +00:8.73 this chapter, is working with external packages or libraries. +00:12.25 There's a place called PyPi, or Python Package Index, +00:15.86 and we're going to use this tool called "pip" to +00:18.22 access all the libraries there, and we can just plug those right into our application, +00:22.18 and there's literally hundreds of thousands of libraries out there that we can use, from +00:28.35 computer vision to machine learning to working with stuff on the Internet and calling +00:32.35 API's and so on. +00:33.49 So that's what we're really going to focus on, using these external libraries to make +00:37.99 working with this weather data on remote servers +00:40.97 super easy and straightforward. Python is often referred to as having batteries included. +00:46.13 That means it has so much contained within it. +00:48.43 Rather than writing a bunch of functionality, +00:50.97 you could just grab little pieces and put it together. +00:53.31 A lot of times that means working with something from the standard library. +00:57.44 But even more than that, this PyPi place with hundreds of thousands of libraries +01:1.82 are also right at your fingertips. +01:4.2 So if you need to build almost anything, +01:6.78 there's probably a library that either does that, or it will help you much more easily build that +01:12.09 thing. When we talk to our weather service, +01:14.74 we're gonna create an http client, that's basically a major part of our application, to go +01:19.18 out there and do a web request. +01:21.0 We're gonna use API endpoint. That API is gonna return a certain type +01:25.05 of data called JSON, which is very, +01:27.35 very popular in API's. +01:28.91 So we're gonna work with JSON and Python. In order to install our external packages correctly +01:34.21 and isolate them on a per application or per project basis, +01:37.65 we're gonna work with these things called "virtual environments". +01:40.23 The actual library that we want to use +01:42.58 to call our API, +01:43.89 to build the HTTP client is something called "requests". +01:46.66 It's one of the most popular packages in the world, in the Python space. +01:51.18 I believe it's downloaded about seven million times a month, +01:54.29 so yeah, no joke. It's very popular. +01:56.34 And finally, we're gonna be passing multiple pieces of data from one function to another +02:0.74 and we'll see that there's this concept of tuples, +02:3.99 these data structures that hold more than one thing. +02:6.75 We'll be able to pass multiple things around using these tuples. +02:10.02 However, they're a little bit clumsy, +02:11.65 so we can use this more improved version that comes from the Python standard library called +02:16.79 "named tuples". You're going to get a ton out of this chapter, +02:21.01 and it's gonna be a lot of fun to see how all these pieces fit together. diff --git a/transcripts/txt/05_app/04_app_start.txt b/transcripts/txt/05_app/04_app_start.txt new file mode 100644 index 0000000..7525886 --- /dev/null +++ b/transcripts/txt/05_app/04_app_start.txt @@ -0,0 +1,37 @@ +00:0.14 Well, let's create our application, +00:1.95 our project, to build our weather client here. We're gonna say "new project", +00:6.17 and we're gonna create it in the "weather_client" folder on my desktop, +00:9.14 then I'll move it into the GitHub Repository. It's gonna be using the Python 3.9 +00:12.81 interpreter, just the system one for now, +00:14.63 we'll talk about what that means in a minute, +00:16.29 and I'm not gonna use this main script, welcome script, +00:20.32 that PyCharm just added to its functionality for new projects. +00:23.75 Over here we have our empty project and I'm gonna add, +00:27.06 as we do for all of these, a new Python file and we'll call it "program". +00:29.91 Then just to get the little button lit up here, +00:33.91 let's go ahead and, once it's done +00:36.25 indexing, right click and say +00:37.61 Run. Run it, and of course it does nothing, +00:40.56 but you can see it's now ready to run with the hotkeys or with that little button +00:44.34 there. So what are we going to do in our program? +00:46.55 Let's just write these out as steps. +00:48.91 Well, we're going to show the header and we're gonna get the location requests, or +00:54.73 the city, where do you want to get the weather for. We're gonna convert what they type +00:59.5 in into more useful data. What they're going to type is something like, they're gonna +01:4.53 enter, you know, maybe Portland, +01:6.21 Oregon as a whole single string here. +01:8.99 And what we want to know is both +01:10.16 Portland is the city, and Oregon is the state, +01:12.77 and the default is that the country is the United States or something like that. +01:17.14 So we're gonna convert plain text over to data we can use. Once we have that data +01:24.02 for the city and the country and state and all those sorts of things, we can +01:28.26 get the report from the API, the actual weather API that we're +01:32.66 going to use, and then the last thing, we're gonna report the weather. What we're +01:36.49 gonna get back from the API +01:37.72 is structured data, and we want to take pieces of that data and turn it +01:41.36 into something that's friendly to humans. +01:43.32 Like "the weather is gonna be 57 degrees and sunny", +01:46.42 Not "here is some +01:48.3 data structure that the API returned". +01:49.92 So that's what this last thing here is going to be about. +01:53.54 So these are the steps that we're gonna have to take to build this simple application. diff --git a/transcripts/txt/05_app/05_main_method.txt b/transcripts/txt/05_app/05_main_method.txt new file mode 100644 index 0000000..f53c1e7 --- /dev/null +++ b/transcripts/txt/05_app/05_main_method.txt @@ -0,0 +1,53 @@ +00:0.14 Before we get into writing the code that goes behind each one of these steps, +00:3.7 let's do just a little bit of organization. +00:5.61 Remember, I always like to structure my program with a main method that has the +00:9.27 high level steps of what it's going to do right the top. +00:12.11 So let's write that, and we'll put these steps here. Now, notice there's a little error +00:17.35 right there, if I make it go up +00:18.94 it'll probably move to the bottom. +00:20.2 No, it still stays there. The error is that the main method doesn't actually do anything yet, +00:24.88 so let's just make it do something. +00:29.84 "Hello from weather main". Okay, +00:31.39 so now there's a meaningful main implementation here so that the structure of the Python code +00:36.65 actually works. However, remember, +00:38.82 if we run it, there's no output. +00:40.65 What happened to our "hello weather main"? So that's because we're not actually calling it, right. +00:45.22 That's just defining a function that if it were to be called, +00:48.2 it would say "hello from weather main". +00:49.72 So if we call it like this, +00:50.87 that's great. However, this will not allow us to reuse the functionality of this +00:57.06 script or this application. So, +00:59.01 like get a report from the API? +01:0.74 Nope. We will not be able to use that function because if we import this, +01:3.3 it's going to actually, as part of the import statement, +01:6.55 processing, is going to run this. +01:8.19 So, recall there's this convention, "if __name__ == '__main__'" +01:14.51 that means it's being run explicitly, +01:16.56 not imported, so we should do the same thing. +01:19.34 But if somewhere else we import it, +01:20.77 it'll just define the functions and not try to run the program. +01:23.78 This actually is so common, this convention right there, +01:27.84 in most real Python programs, that PyCharm actually has what's called a "live template" +01:33.94 for it. So if I type the word "main" +01:36.49 you can see there's this thing that's a function, +01:38.5 that's just what I wrote above, +01:39.53 but there's another one down there with a little hint that's like "if name equals main" +01:43.47 hmm, maybe that's interesting. So if we hit Tab here it's going to expand that, +01:48.47 that live template will expand that, and then we can call the actual function we're +01:53.13 looking for, Okay? And it doesn't matter what this is called. This could be called +01:58.34 something like "entry point" or whatever, +02:1.34 right? It just means if we're being run as a program, run the thing that +02:6.39 we're thinking of as the top level. +02:8.34 Here we go. Okay, So let's look over here. +02:13.04 And if we look actually in the editor, +02:16.34 you can see there's a whole section of these live templates for all the different languages. +02:19.6 If you're doing SQL or you're working on Flask or you're doing Javascript but +02:24.58 most relevant to us right now +02:25.89 is Python. And down here you can actually see "main". +02:30.74 So this is the template that we were expanding, and I actually went +02:33.33 and created my own that will create the main method and write that. So I +02:36.69 can just start from an empty file and type "F-main" so you can create new +02:40.51 ones as you see fit. So, +02:42.97 for example, the one I created would do this part right here. +02:46.06 Saves me a little bit less thinking about it, just get started and go. So these live +02:50.54 templates are super helpful, and now our program is ready to be run as an application. diff --git a/transcripts/txt/05_app/06_get_the_location.txt b/transcripts/txt/05_app/06_get_the_location.txt new file mode 100644 index 0000000..3650673 --- /dev/null +++ b/transcripts/txt/05_app/06_get_the_location.txt @@ -0,0 +1,37 @@ +00:0.04 Well, let's get started writing the pieces +00:2.21 we got to write to build our applications. +00:3.97 So the first one is, we'll just have a +00:5.65 print or show header, I guess we'll call it "show Header" and we'll come down +00:10.48 here and write it like "def show_header()", super easy. +00:14.32 And remember, all the headers they look like, we'll add a couple of sections, and we'll +00:18.25 say, whether client or weather app or something like that, +00:22.74 we'll roughly try to center it and then maybe just a new line at the +00:25.92 end. Let's get rid of that part. Now +00:28.81 if we run it, you can see our little headers coming out the bottom. +00:31.5 Great. The next thing we need to do is get the actual location. +00:35.77 We'll call it "location_text". +00:37.29 Remember, this is not the location data yet. +00:39.75 This is like human friendly plain text, +00:43.08 right? Stuttgart, Germany or Portland, +00:45.59 Oregon, U.S.A., something like that. +00:47.32 So we're gonna just get that from the user. We can just do a simple input +00:51.03 here. So we can say "where do you want the weather report" and put a +00:55.24 little message, and then we'll put this space here so they're not typing directly on that +00:59.94 text, and let's just print out "you selected", +01:3.74 Now we could go like this, "location text", +01:6.23 but if that came back as none, +01:7.85 for some reason that would crash. +01:9.29 And we could also do ".format +01:12.0 (location_text)" and put that there. +01:14.77 But now that we're using Python 3.9 we can actually use what's called f-strings. +01:18.33 We put an f here, notice the color of the curly braces change and we +01:21.94 can just put the thing we wanna format like that. +01:25.24 So here's a little cleaner, simpler thing what we might do. Okay, +01:28.72 let's just see that this works. +01:29.78 Okay. Where do you want? +01:30.66 I'm thinking Boston, U.S., You selected Boston, +01:36.18 or I'm thinking maybe Boston, Massachusetts, USA, you selected Boston, +01:43.3 Massachusetts USA. Alright, well our getting this information from the user works. +01:49.14 Next thing to do is convert it over to plain text, +01:52.24 hand that over to the API and then convert the API response +01:55.68 over to some kind of human readable thing like "the weather is whatever it happens to be". diff --git a/transcripts/txt/05_app/07_weather_api_overview.txt b/transcripts/txt/05_app/07_weather_api_overview.txt new file mode 100644 index 0000000..6a2965d --- /dev/null +++ b/transcripts/txt/05_app/07_weather_api_overview.txt @@ -0,0 +1,46 @@ +00:0.04 Let's have a quick look at this real time +00:2.48 weather API that we're going to be using. +00:4.4 So, there's a lot of weather API endpoints out on the Internet. +00:8.09 In fact, one that I think is really cool is "openweathermap.org". +00:11.0 So if you're going to really try to create a live weather application, +00:14.99 go use theirs. This one is just for educational purposes, +00:18.25 has limited data and so on, +00:19.37 but it will give us the answers generally that we're looking for here. +00:23.47 So this is a HTTP RESTful +00:26.51 API. And what that means is we just make HTTP requests, like standard +00:30.15 web browsers do, and what we get back typically, not always, +00:33.47 but typically is some kind of formatted data called JSON. +00:37.39 So, what we can do is we can go over here and say "weather.talk +00:40.92 python.fm/api/weather" +00:45.01 and it requires that we pass some things. +00:46.86 So if we don't pass a city, +00:48.22 it says "missing field: City" so we could put "Boston", Sorry, +00:53.31 we could put "city = Boston" like this, +00:56.37 there we go. And apparently, the weather is mist. +00:59.6 Some kind of mist rain, I don't know. +01:1.61 It should be category: rain, description: +01:3.53 mist. Who knows? Here you can see, +01:4.95 we're getting the weather. It's right now 13 degrees Celsius because down here somewhere we +01:11.27 have our units are metric. If we wanted this to be in more US style +01:15.29 we could say +01:16.49 "units = imperial". Now all of a sudden +01:19.68 it's 55°F instead of whatever it was Celsius. +01:23.94 Okay, So what we do is we make a regular request and we get data +01:27.01 that looks like this back from it. +01:29.67 So this is called JSON, and we'll see how to play with it in Python +01:33.27 really soon. But it's a nice, +01:34.84 simple format That's very, very natural for us to work with over API. +01:38.71 And if we come back here, +01:40.2 you can see that we have a city, +01:41.85 right? This should have a question mark. +01:43.3 actually. It says that we have a city equals whatever we want, +01:46.64 but we can optionally pass a state, +01:49.23 if you want to disambiguate +01:50.47 say Portland, Maine, from Portland, +01:52.09 Oregon. We can pass a country we wanna disambiguate +01:55.58 Portland, Australia, from Portland, +01:57.66 USA. If you don't specify a country, +01:59.15 it defaults to the US, Just that way. I could type in short city names and +02:2.47 it's still gonna work. Then also you saw we can pass in units as metric, +02:5.88 imperial or standard, and it defaults to metric. +02:9.84 So this is the weather API that we're going to be working with. diff --git a/transcripts/txt/05_app/08_json_whos_that.txt b/transcripts/txt/05_app/08_json_whos_that.txt new file mode 100644 index 0000000..accb353 --- /dev/null +++ b/transcripts/txt/05_app/08_json_whos_that.txt @@ -0,0 +1,71 @@ +00:0.14 We saw that our API returns JSON, +00:2.83 and this is not by coincidence, +00:4.86 this is the most popular data exchange format on the Internet, +00:9.09 period, when one application talks to another, +00:12.43 not just in Python, but across all the languages. +00:14.77 So let's just go play with this idea really quickly so that we know how to +00:18.57 work with it. So let's create a new thing, we'll call it "example_dict" for dictionary. +00:22.01 We'll see where that goes +00:23.58 in a minute. I'm gonna use my fmain magic to find the program. +00:27.7 It's gonna run here. So what we want to do is we want to talk +00:30.82 about this data structure called a dictionary, and a dictionary +00:33.74 looks like this: it has curly braces, +00:36.29 it has a key, so maybe this is gonna be city, +00:39.74 and then it has the value, +00:41.3 the current value of the city. +00:42.75 So this is like a variable type of thing, +00:45.04 and then the value might be Portland, and then the state, +00:49.0 maybe the state is going to be Maine. +00:53.14 And then, we could also have other, +00:55.08 more interesting things in here. Like we could have a +00:58.44 details and the details could actually be a +01:1.42 list of cold, snowy, winter, I don't know. +01:7.41 Maybe it's wintertime right there when we're doing this. +01:9.74 So these are the things that we could have in there. In order to work with +01:12.62 this, first of all, if I just print this out, and run this, +01:17.81 p, d, there we go, if we print it out, look how this looks. +01:20.41 See that right there? If we go back over to our API +01:22.98 here and we click on our example, +01:24.98 we look the raw data, those are basically identical. The only difference that you'll find is +01:30.97 Python's representation have single quotes if you print it, +01:34.18 JSON has double quotes. Other than that, +01:37.23 they're basically the same. Yeah, +01:38.51 there's other technical small details like, +01:40.75 for example, datetimes can't be represented in JSON documents where they can in python +01:45.72 dictionaries. But, think of this JSON stuff as like a subset of these Python dictionary +01:51.03 data structures. If I want to get something from it, +01:53.3 I can come over here and say +01:54.52 like the city, we could print out just the city, notice Portland comes out. +01:58.12 If I asked for something that's not there, like country is not here, +02:1.24 and I use the square brackets to access it, +02:3.32 I get a KeyError. There's nothing called country in here. +02:6.44 So, oftentimes a better way to do this is come here and say "get", +02:10.34 so just do the brackets, if you say "get", +02:11.99 you'll get either, you'll get the thing that you are asking for or none. +02:15.55 You can even pass a default. +02:17.47 So if they don't ask, they don't provide a country, +02:20.07 let's default it to the USA, and that way we'll get the default value here if +02:24.4 this does not exist, but if a country were to exist, like country is Germany, +02:28.69 say now, Germany would come back, in Deutschland, +02:32.55 okay? And then also we can modify these things. +02:35.0 We come over here and set the country to, set it to Australia, +02:39.22 and then we could ask for it again. +02:41.27 Notice, now it's been set, and actually if we just print the whole thing out again, +02:44.21 we get, over here our country is Australia. +02:47.82 Cool, right? What we can do is we can actually just take this, +02:50.89 and it's better to take the pretty printed version just for your own well being, +02:54.79 and we can go over here and say, +02:56.1 "the weather is, paste", you just indent it. +02:59.07 But Python already understands that. So, if we want to get, +03:1.9 say, the weather, let's say the forecast and the temp. +03:5.83 So first we've got to get the forecast and then we get this little baby internal +03:9.69 sub-dictionary back, which we can then ask for the temperature which will give us +03:13.03 that. So we could do a print, +03:15.8 go to our weather and say "get the forecast", +03:19.68 then we're gonna say "get the temp" and that should just print out 64.08 degrees +03:26.24 Fahrenheit, and it does. Okay, +03:27.84 so this data structure is what we'll be exchanging with the +03:31.84 API, we're gonna be passing these JSON strings back and forth, +03:36.04 but you'll see that Python has it, because they're basically subsets, sub-types, +03:41.65 the functionality of Python dictionaries, It's incredibly easy to convert that response over to a Python +03:47.1 dictionary, as we've explored right here, and then just directly worked with that. diff --git a/transcripts/txt/05_app/09_convert-plaintext_to_data.txt b/transcripts/txt/05_app/09_convert-plaintext_to_data.txt new file mode 100644 index 0000000..e87a0a7 --- /dev/null +++ b/transcripts/txt/05_app/09_convert-plaintext_to_data.txt @@ -0,0 +1,114 @@ +00:0.14 Recall now that you've seen the API, +00:1.67 what we have to do is we have to pass over to the URL: +00:4.88 city equals city value, country equals country value. +00:9.91 But that's not what we're getting here. +00:11.84 We're just getting plain text that looks like "Portland, USA" +00:14.74 we can't say the city or the country is "Portland, USA", +00:18.96 It's got to be broken into pieces and understood, +00:21.65 right? So that's what we're gonna do now. +00:22.99 We're going to convert this plain text over to data. +00:26.15 So, let's just put for now, +00:28.63 location, I'll write that here in a second, and for a second and then we'll improve upon +00:33.03 it. We're gonna go over here and say +00:33.99 "convert_plaintext_location()" and this will be the location text. +00:38.62 So we gotta go write this function and it's gonna be some cool string manipulation. +00:42.96 And let's just print out as an F-string +00:44.86 "the location equals the location", what we got back. +00:51.74 Alright now, First of all, +00:52.82 if for some reason we get no location at all, +00:55.65 we don't want to try to process it. +00:57.18 We're just going to return nothing. +00:59.59 So if there's no location specified or it's empty or something like that, +01:6.44 then we just want to say +01:7.31 "you know what? There's no location contained in empty text". +01:11.36 This is false if its both either none or just the string quote like this. +01:16.8 But it could just be like a space or a tab or a return character +01:21.48 sometimes it also doesn't mean much, so this strip will take that away. +01:26.04 And we'll say "If there's not anything here, +01:27.92 we're gonna, either it's truly nothing or it's effectively nothing, +01:31.61 we can't process that, so there's no location". +01:33.58 So now we're in a good place to say things like location text equals first; +01:38.53 maybe you wanna get the lower case version of this and we also don't want any +01:43.03 extra spaces there. So we could be very restrictive about what we allow people to +01:48.26 type up here. We could say "you must type city comma state comma country". +01:54.21 That gets a little tricky because not every country has a state concept +01:58.4 That's easy to type that goes over to our API. +02:1.07 So you could say, Well, +02:2.35 you just type city and country, +02:5.04 but then lots of countries have duplicates. Like there's at least three Portland's; +02:8.83 there's a Portland, Oregon, +02:9.74 Portland, Tennessee, and Portland, +02:10.9 Maine. So we're gonna need a little bit of flexibility here. +02:13.55 We have basically three cases: is it just the city? is it the city +02:17.17 and the country? or the city, +02:18.5 state, and country? Now, regardless of which of those we have, +02:22.02 let's just print out "location_text" and run that real quick here, +02:25.97 and we'll just return a "location_text" +02:28.25 back for the moment. If we run this and I put Portland, +02:33.03 USA, notice we're getting "Portland lower case usa" because we did the lower and +02:38.18 strip and if there were spaces, +02:39.39 it would take those way away as well. +02:41.25 But what we need to do is we need to somehow get Portland and USA, +02:45.02 and Python has really cool mechanisms to take a string and break it apart. +02:49.94 So let's say parts is going to be "location_text.split()" split means split into +02:56.52 parts, and then you give it a character on which you want to split. +02:59.6 So we'll say comma, and now let's just print out parts here, +03:3.41 see what we get back. So if I say Portland, +03:6.91 Oregon, USA like that, look +03:8.97 now we have a list of three strings: +03:11.22 Portland, Oregon, and +03:13.74 USA. And there's comments in here, +03:14.89 but these commas are not in the string. +03:16.49 These are just the list saying I have things that are in it, +03:19.59 right? The list represents itself with those separators. +03:22.36 There is a space right there and a space right there that we need to deal +03:25.78 with. But other than that, +03:27.36 we have the three parts. So here's the deal is, do we have just the +03:31.4 city, the city and the country, +03:33.44 or the city, state and country? +03:34.98 And the way that we're going to figure that out is how many parts do we +03:38.07 have? So if the length, how many parts are there, equals one, +03:43.23 well, then it's just the city. +03:45.27 So we're gonna say "city equals nothing, +03:48.04 state equals nothing, country equals nothing". +03:51.68 And then, in this case, +03:52.6 we're gonna say "city equals parts of zero", +03:57.45 right? That's how we get the element out of the list, +03:59.28 we say the first one, zero based. +04:1.22 And then we say "strip()", because we want to actually remove those spaces. +04:5.58 Remember, Oregon had a space on the front +04:7.82 if they typed it and things like that. +04:9.94 And then we want to say "country equals US", +04:13.19 two letter abbreviation, and actually, +04:15.33 we can just put that as a default up there, +04:17.74 Alright? On the other hand, +04:19.82 if the length of the parts two, then they've said the city and the country, what +04:24.74 we have assumed here, so this is gonna be the same, +04:27.1 but the country is going to be the next one in the list. +04:30.93 Parts one, if they specified it all the way out +04:34.24 and it's three, then it's city, state, country is the format that we're expecting. +04:39.94 And if it's not one, two or three, +04:41.78 it must be some kind of error, +04:43.37 so we're going to return "None" again. +04:45.71 Alright, Now, instead of returning this location text, +04:49.32 we're gonna deal with that in a second. +04:50.53 Let me just print this out here and now we can show things like "city equals +04:56.01 city, country equals country". And this is exactly, I guess I'll put state in +05:2.74 here, this is exactly the style of format of data that we're going to need +05:7.14 to pass it over to the API. +05:10.64 Alright, now let's run it. +05:11.64 Let's just say Portland, city is Portland, State is empty, country is +05:15.63 US, perfect. If we say Portland, USA, +05:21.84 We have, city is not what we're looking for but country is good. +05:25.4 What did we do wrong here? +05:27.24 Oh, it's because I didn't I didn't put a comma, +05:29.66 I just wrote it wrong. Here we go. +05:33.78 City is Portland, country is USA, and then let's do the full thing: +05:37.71 Portland, Tennessee, USA let's say. Now we've got city is +05:42.49 Portland, State is Tennessee, and country is USA, perfect. So we're breaking this apart correctly, +05:47.72 but now we have this challenge. +05:50.94 Like you could see we can return one thing, we have to return more than one +05:54.18 thing. How do we get all this stuff back? +05:55.49 So we're gonna address that in just a minute. +05:57.46 But just breaking the data apart +05:59.39 is already done, we just gotta figure out how to pass it around in our program. diff --git a/transcripts/txt/05_app/1.txt b/transcripts/txt/05_app/1.txt deleted file mode 100644 index 2ae1502..0000000 --- a/transcripts/txt/05_app/1.txt +++ /dev/null @@ -1,53 +0,0 @@ -
0:00 Hello and welcome to app number 5.  -0:02 This time we are going to build a weather client,  -0:05 it's going to go out and actually tell you  -0:07 what the weather is in your location.  -0:09 Now this is not a weather client simulation, -0:12 it's a real live weather client.  -0:13 So it's going to go out to the internet,  -0:15 find some live data, pull that back and display to the user.  -0:18 It's going to be a lot of fun.  -0:19 So, what it's going to look like?  -0:22 It's going to have a header like all of our little apps do, saying weather client -0:26 and then it's going to ask the user for their zip code -0:28 and they are going to enter zip code -0:30 it will go out to a particular place on the internet  -0:33 we'll talk about it in a minute,  -0:34 find it'll lookup that zip code, get the weather,  -0:38 get the forecast and display it back to the user.  -0:41 So here you saw I entered 01010  -0:44 and it said the weather in Brimfield Massachusetts is 32 F and clear. -0:49 And at same time the weather in Florida is 78.1 F and overcast.  -0:55 So we are going to build this app and we are going to learn a lot. -0:58 The primary focus, the lesson to take from this application  -1:02 is really this concept of external packages,  -1:05 Python is an ecosystem described as "batteries included" -1:10 and there are sort of two levels to this meaning here -1:12 but the idea is that Python comes with everything that you need to do your job -1:17 everything you need to work with networks,  -1:20 HTTP, scientific results, images, you name it.  -1:24 And at one level that's the standard library built into Python -1:27 there is a ton of modules and features there,  -1:30 but the sort of outer shell of that is even more amazing  -1:33 there is this place called PyPi or the Python package index,  -1:37 and in this package index there are many thousands of packages or libraries  -1:43 that you can download and very easily add to your application.  -1:48 The tool that we are going to use to do this is called pip,  -1:50 so we'll talk about how you get that, how you use it.  -1:53 The two extra features that we need to bring in  -1:56 as an external package for our app are HTTP client,  -1:59 how do I go out to the internet and grab some data and download it,  -2:03 and screen scraping, how do I take semi structured  -2:07 maybe malformed but mostly correct HTML  -2:10 and pull that apart and understand it  -2:13 and pick up just the little elements that we need -2:15 and that's called screen scraping.  -2:16 For the HTTP clients we are going to use  -2:18 very popular library called Requests -2:21 and for screen scraping we are going to use something called Beautiful Soup.  -2:24 We are also going to talk about this concept of tuples -2:27 which is returning multiple items from a method  -2:30 and a slightly nicer more usable version  -2:33 that you can use called named tuples.  -2:36 All right, let's get onto building that app! - diff --git a/transcripts/txt/05_app/10.txt b/transcripts/txt/05_app/10.txt deleted file mode 100644 index ed5cae8..0000000 --- a/transcripts/txt/05_app/10.txt +++ /dev/null @@ -1,54 +0,0 @@ -
0:00 Now that we have the HTML downloaded,  -0:02 it's time to use Beautiful Soup to pull out  -0:05 just the elements that we want to work with.  -0:08 Now what you kind of see is what many of you probably know, -0:11 that the web is a messy, dirty place,  -0:14 there is all sorts of malformed documents  -0:17 and weird white space and odd formatting that we are going to get back,  -0:22 so there is going to be a lot of cleanup going on here,  -0:24 but this Beautiful Soup package is really going to help us. -0:27 So we get started using Beautiful Soup -0:29 with an import statement, we just say BS4.  -0:31 So we are going to write the function parse HTML  -0:35 and we are not going to worry about the return value right away,  -0:38 so let's just say parse, no let's make it be the more purpose driven,  -0:42 get_weather_from_html() and we'll give it the html,  -0:47 so let's go write this function, down here,  -0:50 so our goal is to take this HTML which is dynamically generated  -0:54 by the server including the weather report  -0:57 for whatever the weather is in the zip code that the user entered.  -1:01 But there is a known structure to this  -1:04 and the way that we work with the structure is really  -1:08 the same way that people style web pages and layout web pages,  -1:11 it has to do with the css, we are going to start  -1:14 by parsing this and then we are going to go explore css classes  -1:18 and ides and stuff form this HTML  -1:20 and then we'll use that in code to pull out the little bits that we need.  -1:24 Start by creating something called a Beautiful Soup class,  -1:27 so we say bs4. beautiful soup,  -1:30 now notice how I can just type BS and it actually finds  -1:34 all of the things that have sort of this camel casing capital B capital S -1:39 that's super handy and we are going to give it this HTML.  -1:41 Here is the big step, we are going to take the text and parse it  -1:44 into an n memory dome so that we can ask questions,  -1:47 and let's just make sure that this doesn't crash on us,  -1:49 so we'll print out the soup, over here and we'll just enter 97201 wait for a moment,  -1:54 and boom- out comes what we were looking for.  -1:57 It turns out when you print the soup object,  -2:00 it just turns it back into a string that represents the document,  -2:03 so it looks like that worked. -2:05 Ok, so we are off to a great start but I noticed something that maybe I didn't expect,  -2:09 like a little warning came by, so let's comment out  -2:11 that little print statement and do this again.  -2:15 And Beautiful Soup now has a warning that says we are choosing  -2:18 what we think is the best parser for this particular system  -2:22 and this might be a different answer on a different machine,  -2:24 so get potentially non deterministic results  -2:27 so it says why don't we make this very explicit and put that here  -2:30 so now we should get no more warnings and be ready to carry on.  -2:35 So everything is running good, just do a little reformat in here,  -2:39 so the next thing that we need to do is if we look at this soup object  -2:42 you'll see there is all sorts of find methods here  -2:45 and we can use this finds to navigate the tree and pull out the elements -2:49 that we need but what do we put in there,  -2:52 that's where we go and we inspect the HTML. \ No newline at end of file diff --git a/transcripts/txt/05_app/10_multiple_values.txt b/transcripts/txt/05_app/10_multiple_values.txt new file mode 100644 index 0000000..c53029e --- /dev/null +++ b/transcripts/txt/05_app/10_multiple_values.txt @@ -0,0 +1,50 @@ +00:0.14 Well, here we are. We've got our city, +00:1.93 state, and country, but our function over here, +00:4.83 it just returns one thing: a +00:7.04 string. Or something else, a number or whatever. Functions just return +00:11.14 one thing, actually, sorry, +00:12.4 this one, but same idea: returns one thing. +00:16.04 How can we take all that data that we've gathered, the city, +00:18.73 state, and country and return them back? +00:20.34 Well, what we can do is we can create something called a tuple. +00:23.62 So a tuple, we'll call this "loc" for a minute, +00:27.04 the way we create this in Python, +00:28.67 we would just say "separate things by commas", +00:32.34 and then we could actually return that one thing like this. +00:35.85 Let's see what we get if we do this. +00:37.28 We say Portland, Oregon, USA. +00:40.88 Look what the location is. It looks a little bit like a list, +00:43.54 but it has parentheses instead of square brackets, +00:45.49 which means you can.. it means it's a tuple. +00:48.24 but the limitations are you can read the values of it, +00:51.24 but you can't add to it and stuff. +00:52.76 But other than that, you can kind of think of it as like a list +00:55.06 a little bit. So this is cool. +00:57.22 And then up here, we have to say we get our one location, +01:1.0 but what we really want is the city, +01:3.38 state and country. So on the other hand, +01:6.05 on the reverse side, once I have a tuple, +01:8.54 I have this really cool thing +01:9.51 that I can do in Python, It's called tuple unpacking. So I can say +01:12.73 "city, state, country equals location". +01:16.79 And then let's just print out these things here, +01:19.14 see what we got. If We come over here in Portland, +01:24.14 Oregon, USA, look what we get when +01:26.2 we print this out right there. +01:27.33 That's those three things just with a space between them, +01:30.55 that's how print works if you just put multiple things into it. +01:33.12 But they all came back into city, +01:35.53 state and country. We could even put a break point right here, at the debug, +01:40.24 and notice city is Portland, state is Oregon, +01:42.96 Country is USA, perfect. Now, We don't need these intermediate steps, +01:46.97 Actually. I just wrote them +01:48.68 so it's really clear what was going on. +01:50.45 So what I want to do is I just want to say for the moment, +01:53.31 city, state, country equals that, and the tuple unpacking will just happen automatically, +01:58.54 and then down here we created the location and we returned it. +02:1.63 We can also just do you like that. +02:4.96 We don't need this anymore, okay? +02:6.43 So one more time. Here we go. +02:10.13 We got them back exactly as we had hoped. +02:12.71 So how do we return multiple values from here? +02:15.57 We just return a tuple. diff --git a/transcripts/txt/05_app/11.txt b/transcripts/txt/05_app/11.txt deleted file mode 100644 index d27c839..0000000 --- a/transcripts/txt/05_app/11.txt +++ /dev/null @@ -1,49 +0,0 @@ -
0:00 Now, the big question is in that huge HTML document  -0:04 how do I get hold of this piece, this piece,  -0:08 that little bit right there and that bit, and that bit, independently.  -0:14 So, like I said the key to this is a css  -0:17 and let's try to use our web browser developer tools to figure this out.  -0:20 So we can come over here and right click on Portland and say inspect element,  -0:23 so here you can see and there is dev app here we've got an id of location,  -0:29 so we would start with location,  -0:31 and then we have this contained within in H1  -0:34 and then there is actual element that we want.  -0:37 So we could write some code that comes in here and grabs this element as div  -0:40 and looks inside of there for H1  -0:42 and within the H1 it's going to grab just the text.  -0:45 Now, if the website we are working with happens to use jQuery  -0:50 it will let us test this in a really nice way  -0:53 so remember id location let's go over here to the console  -0:56 and the way you can tell if this is jQuery or not is -0:59 you hit $ and then you hit enter,  -1:01 if that doesn't come up as not found or none or something  -1:04 then you have jQuery so this lets us test our expressions.  -1:09 So I can come down here and say I am looking for a div with the ID and css -1:13 you say # id and that was the location  -1:16 and then a sub item so put a space H1 -1:20 and I can just write out the text and see what that is  -1:23 let's see if this will work, boom,  -1:25 ok so if I was given that in Python I could definitely do a little string work  -1:30 to get that out of it, so this div#location is what we will work  -1:35 and in fact we could keep it simpler like this,  -1:37 just # location for the id of location and then space, subitem H1. -1:42 Similarly let's look at the other ones here.  -1:44 So overcast, inspect element, this is going to be within current condition,  -1:49 and if you look at current condition, you can see it has this wx value inside of it,  -1:56 it has the id of current condition and within there we had a class,  -2:01 notice the class of wx value. Now class is in css,  -2:06 I start with the dot, remember, id class and we put a space  -2:11 to say the thing with id current condition contains the thing with class wx value  -2:16 and then we can do text and see what comes out there.  -2:19 Overcast, ok, that is looking good,  -2:21 and if we go back here and look at this one, -2:24 we have current temp wx data and then value  -2:28 and unit for those two pieces,  -2:30 so we are going to be able to use all that information to grab exactly the little bits  -2:33 that we want right there, out of the html.  -2:36 So just for a reference here, let me paste these, -2:40 which I have grabbed and formatted for you,  -2:43 so we have them right here to work with while we are building this method out.  -2:46 So the next thing to do will be to use Beautiful Soup  -2:49 it'll actually do something kind of like we did with jQuery  -2:52 and the browser but we are going to do it in memory in Python. \ No newline at end of file diff --git a/transcripts/txt/05_app/11_namedtuples_better_than_plain.txt b/transcripts/txt/05_app/11_namedtuples_better_than_plain.txt new file mode 100644 index 0000000..c567612 --- /dev/null +++ b/transcripts/txt/05_app/11_namedtuples_better_than_plain.txt @@ -0,0 +1,52 @@ +00:0.14 Now, there's a problem with these tuples that we're returning here and let's go back +00:4.32 and actually put this into a variable for a minute +00:6.63 as we saw say this t tuple is this. +00:9.35 Now when we go to it, +00:11.12 notice it knows it's a tuple because you can count how many things are in +00:14.18 it and so on. And the way you access it is kind of like a +00:16.57 list or something. So you could say, +00:17.9 "Give me the country would be that, the state would be this, and the city would +00:22.33 be that". Well, that's not at all obvious that it's city, state, +00:26.11 country. What if it was city, +00:27.94 country, state? Because state is optional. +00:30.24 There's no communication here about helping us with this, +00:33.37 Okay? So this is a real big drawback of these tuples and passing them +00:36.69 back like so. They don't know what goes where. +00:39.54 So Python has a couple of mechanisms, or a couple of data structures, that make this +00:43.36 way, way better. We could use classes which require a lot of stuff for +00:47.9 us to talk about. But if you're looking for just a short, +00:50.17 sweet little thing like this, what we can do over here, is we go over here and +00:53.6 say "import collections" and then we can define a special tuple that gives it extra +01:0.36 properties. So I can say a "location = collections.namedtuple()" +01:5.57 and notice the first thing we put us the type name, +01:8.01 which should be the same as a variable name, +01:10.45 right? So this is what we program against, +01:12.52 and this is what it thinks its own name is, +01:14.16 so they should be the same. +01:16.14 And then here we just put what we think it has: +01:18.27 city, state, country, and the order here is the order +01:22.17 you specify them. So watch this. +01:24.23 We can go over here and use this type down here. Instead of passing back a +01:28.93 regular tuple, we would say do the same thing, +01:32.0 but notice the location is gonna take a city, state, country, so we could do like +01:36.46 this. There's the location, and I wanna leave this here for you +01:40.5 so you have it. But let's say, we're going to return a +01:44.36 location like that, and advantage just to tie it back to this example up here, +01:49.22 now we say "t2.", look, country, state, city. +01:53.24 It knows the things that it has, +01:55.24 doesn't matter what order they're specified in the tuple, you address them by name, +01:59.04 not by order, which is way, +02:0.82 way better. So I'll just put "t2.city" and comment those out +02:5.52 so you have it left in code there. +02:7.54 Alright, so when this comes back, +02:8.79 we could still do this trick of unpacking it, +02:11.78 but let's just go down here and say location again, +02:16.0 and now we can print out location, +02:18.76 which is gonna be looking nicer as well. +02:22.54 See, it's location, the city is +02:23.91 Portland, state is Oregon, country is USA. +02:26.39 So these named tuples make it much easier for us to come back here and program +02:30.1 against. We know it has a city, country, and state. +02:32.29 It doesn't matter the order and so on. +02:33.82 So, really, if you need to pass multiple things back from a function, +02:37.84 tuples and tuple unpacking is cool, but named tuples are probably what you actually want to work with. diff --git a/transcripts/txt/05_app/12.txt b/transcripts/txt/05_app/12.txt deleted file mode 100644 index 7f5166c..0000000 --- a/transcripts/txt/05_app/12.txt +++ /dev/null @@ -1,72 +0,0 @@ -
0:00 Now that we have our css selectors let's go grab those values in Python.  -0:04 So let's start with the city. -0:07 We had used the id of location and then within there, there was a heading H1.  -0:12 So what we want to do is go to this soup object here and we'll say find,  -0:15 now the options for find there is all kinds of options we can pass here,  -0:19 notice at the end there is a **kwargs  -0:22 that means this methods accepts keyword arguments,  -0:26 these are a little bit tricky to figure out what keywords you can use  -0:30 but it's an alternate way of calling functions.  -0:32 So you can see up here we are using what are called positional parameters,  -0:35 saying html is this, the second parameter is the name of the parser to use. -0:39 But down here we are going to say something different, -0:42 we are going to name the parameters,  -0:44 we'll say Id = and then id is going to be location, right,  -0:46 so this explicitly naming the variables here lets us have more flexibility  -0:50 in the way and the order in which we call the function.  -0:53 So this is going to go get that div that contains H1  -0:56 and then within there we would like to get the actual H1 itself. -1:01 And then get the text, so we can use what is called a fluent interface -1:04 this find method takes one of these soup objects  -1:06 but it also returns another one -1:09 there is sort of a sub element that we can just search within the location  -1:12 so we can also say now find,  -1:14 and the thing to pass here is actually the node -1:17 so we can just say H1 for the node -1:20 and let's just print this out let's call this location like that  -1:25 and we just print the lock.  -1:27 Now remember I told you the web was a kind of messy place  -1:29 so you are going to see this didn't come out  -1:32 quite as nice as you would like but that's the web,  -1:35 it looks like we got Santa Ana California, very cool, -1:38 but look at all this other stuff,  -1:40 this is actually just the node H1 that contain that header,  -1:43 if we want just the text we can actually say .get_text -1:47 if I run this again, 92701 is what I had typed,  -1:51 and now there we go, we have Santa Ana California  -1:54 and all of this basically from here down this is all what came back  -1:59 so we are going to have to do some cleanup on this  -2:01 but we are in a good place, where this bit of text is going to work well for us.  -2:06 Now let's do the others  -2:08 so we can say that the condition is going to be soup.find -2:13 remember this is contained within a div  -2:15 which has the id current condition,  -2:18 so we can similarly go down here and say say id = like that  -2:21 then we can say find and this time we are not going to name the element  -2:24 but in fact we have a class wx value,  -2:27 now in Python class is a keyword so they use class_ here like this  -2:31 and we'll say .get_text.  -2:36 And let's print out the condition. -2:43 It's overcast, ok, let's just stop for a minute and see how awesome this is,  -2:48 so we went and basically two lines of code came up here  -2:53 and we downloaded this weather forecast from the internet using Requests  -2:56 and just a handful lines more created a Beautiful Soup  -3:01 which parse the HTML and we used this css selector style of fluent interface  -3:07 to navigate the dome and get the text  -3:10 and what we got is right now in Santa Ana it is overcast.  -3:15 That is really cool and this is all possible because the PyPi  -3:18 the external packages pip to install them, it's really wonderful.  -3:23 So let's go ahead and keep going, finish this up,  -3:25 so we've got our current condition and then we need one for the temperature,  -3:28 we really need two for the temperature  -3:30 so we go down here and say call this temp for temperature,  -3:34 and wx value that should work for us,  -3:37 and then down here we'll have scale as in the unit of measurement,  -3:42 so we are going to have like this, now let's just print those out, -3:45 one way to print the series of strings is to just put them comma separated  -3:48 and Python will print them out one after another,  -3:51 so let's go back to Portland, so 97201, perfect,  -3:55 right now it is overcast, 8.6 C, in Portland, how awesome is that?  -4:00 So the last thing to do here is some cleanup  -4:03 on all these strings and then return them from  this method.  -4:06 So we'll get to that next. \ No newline at end of file diff --git a/transcripts/txt/05_app/12_call_weather_api_intro.txt b/transcripts/txt/05_app/12_call_weather_api_intro.txt new file mode 100644 index 0000000..4de165e --- /dev/null +++ b/transcripts/txt/05_app/12_call_weather_api_intro.txt @@ -0,0 +1,63 @@ +00:0.14 Now that we have our location in structured data, +00:3.09 not just plain text, we're gonna be able to call our weather API. +00:6.27 So let's say "data = call_weather_api" and we're gonna pass this +00:12.26 location over. And as we've been doing, +00:15.92 let's go over here and write this function. Well, +00:18.76 we start out by having our URL, remember we go back +00:23.18 to our example, it's gonna be "weather.talkpython.fm/api/weather" and then +00:28.77 stuff up here. So let's go and work with it like this. +00:32.84 So it's gonna be this base URL up to here and then city, +00:36.25 what goes in here is going to be location dot.. Check out the named tuple, +00:41.01 so cool! Now the state is tricky, +00:43.48 so I'm gonna put the state to the side for a second here, +00:46.47 and the country is going to be "location dot country" like that, +00:53.39 and then the units, I'm gonna just hard code it to Imperial. +00:56.96 You can hard code it to metric or if you want to pass metric or imperial and +01:1.43 let the user decide how they like it, +01:3.34 go for it. But to me I'm just gonna leave it like this because you +01:6.38 gotta pick one, and I'll leave it like that. +01:7.91 Alright, so now it gets a little bit interesting, +01:10.34 because if they pass a state, +01:12.62 we want to use it, but if they don't pass a state, +01:14.59 same state equals blank, might throw off the API and freak it out. +01:18.7 So what we want to do is we'll just do a little test, if location +01:21.91 dot state, then we're going to say "url plus equals ampersand, +01:27.04 make that an f-string as well, state equals state, like that. Location, +01:32.48 not state. There we go. +01:33.4 Okay, So if you pass a state through our structured data, +01:37.12 we're going to tack it on with an ampersand. +01:40.08 The order of the query string, these key value pieces, +01:43.76 this whole thing is a query string, +01:44.85 the order, the key value pairs in the query string, doesn't matter. +01:48.21 So if it goes at the end with units in the middle, +01:50.94 doesn't matter at all. Alright, +01:52.15 so now let's just print out the URL, would call, +01:56.8 and with PyCharm, here's a cool thing with f-strings: +01:59.36 I can type curly brace, and notice the curly brace is still stringy, +02:4.38 it's still green, it's not actually interpreted as anything, +02:6.48 and there's no f at the front. +02:7.78 But if I start to type of variable like +02:11.22 URL, it's still not an f-string. +02:13.3 But if I hit enter, it puts the f at the front and completes it. +02:16.53 So that's pretty cool. Let's just, and I'll just say for now it's supposed to +02:21.92 return something, so I'll say it returns +02:23.32 "None". Functions always return None anyway, +02:25.96 but whatever. Say "Portland, US", +02:29.91 like that. The country and the state need to be +02:33.02 two digits, or two characters. So check it out, +02:35.94 we would call Portland, US with imperial units and let's click it, +02:39.73 see what we get. Look at that. +02:41.17 Broken clouds, 65 degrees. A wonderful fall day here in Oregon. +02:46.49 Now let's call this again, but this time let's get Portland, +02:50.75 Tennessee, actually, yeah sure, +02:53.92 Portland, Tennessee. Like that and says, +02:56.3 notice the state says TN at the end, +02:59.34 and here we have Portland, US, +03:1.21 Tennessee and the weather is 60 degrees. +03:3.5 Looks like it's also nice. Little more clear skies, +03:6.01 but not the same forecast to be clear. +03:8.23 Cool, huh? This is great. +03:10.77 We're actually structuring what we need to send over to the API perfectly. +03:14.76 But now what? This "would call" needs to turn into +03:18.09 "I actually went to the Internet and got data and understood what came back", +03:21.47 but we're off to a great start. diff --git a/transcripts/txt/05_app/13.txt b/transcripts/txt/05_app/13.txt deleted file mode 100644 index e481af9..0000000 --- a/transcripts/txt/05_app/13.txt +++ /dev/null @@ -1,87 +0,0 @@ -
0:01 Ok, so we've downloaded these and we've printed them out,  -0:03 let's go back to printing out the location again,  -0:05 and if I go 97201 get the weather in Portland -0:08 you are going to see the print out is not lovely, here we go,  -0:11 you can see this huge long stream and lots of new lines, -0:14 is actually partly cloudy bit here,  -0:17 so what we want to do is clean this up and now that looks like it printed ok  -0:21 but you know, as the page evolves  -0:23 maybe it has like white space around the edges  -0:25 this is definitely not ok and we don't really need to list the zip code -0:29 because we typed it in we just want this part  -0:31 so let's work on cleanup all these pieces of text first  -0:34 and then we'll pull out just the city and state.  -0:37 So we can come down here and say  -0:39 that the location= cleanup_text of the location -0:43 Now this is a method that we are going to write, -0:45 not sure it justifies its own method but maybe it does,  -0:49 so we are going to come down here and say  -0:51 first of all if you ask us to clean up text  -0:53 and the text is none we try to interact with it  -0:56 that is going to be a crash so we'll do a little error handle,  -1:00 we'll say if not text and use the truthiness of strings,  -1:04 remember if they are empty or none  -1:07 they will be false so we'll just return text,  -1:10 but if they are not empty then we can do some processing,  -1:13 we can say text = text.strip() and strip is a built in method to the string class  -1:18 that will take all the white space tabs, space, new line, \\ end type of things,  -1:24 off of the front and the end, there is also an L strip and R strip, and so on  -1:28 so we are going to modify this and then we'll return the text here.  -1:32 The other thing that we might want to do is if you are new you are like well,  -1:35 how do I know there is a strip,  -1:37 when I hit. does it say strip, no,  -1:40 so we can use something called type hints in Python  -1:43 now these have no effect on the runtime behavior  -1:46 but they will give you better intellisense  -1:49 if I am certain this is a string I can say colon then a class name,  -1:53 I can say str that's the class of a string in Python,  -1:56 if there is an int I could say int and so on,  -1:59 but I'll say str there is string  -2:01 and now when I go down here and interact with this in PyCharm  -2:04 it will actually give me things like strip,  -2:07 they justify L strip R strip,  -2:10 so PyCharm knows now that we told it hey this is a string,  -2:13 it's going to give us help as if it were a string.  -2:16 Again, I could pass a number here  -2:18 and it's not like there will be a compile error,  -2:21 remember there is no compiler, -2:23 but it does help people who use this method  -2:26 as well as the tooling understand what it is you are trying to work with  -2:30 so for sure it's a string we can say str  -2:32 and that will give us some help if you want it.  -2:35 So let's continue here, and do this for the condition,  -2:41 do this for the temp and finally the scale.  -2:45 Those look like they are ok  -2:48 but you never know what is going to happened to them.  -2:50 Now if we run this again, we'll be in a slightly better place.  -2:54 Ok, look, partly cloudy, here is the temperature -2:57 the scale and then we still have this,  -2:59 but that's much better than that huge long ten line bit of text.  -3:04 So the last thing we need to do is actually pull that piece out.  -3:08 So we want to go down here and say we want to do one more thing to the location,  -3:14 we are going to say find the city and state from location,  -3:20 and we'll pass the location here,  -3:21 so this is a function we are going to write,  -3:26 and let's go ahead and get some help form intellisense  -3:29 just so you guys can see it,  -3:31 now the location is a string and we are going to use -3:34 this method here on it called split,  -3:37 so what split does is it will take some character  -3:41 in this case we are going to say \n for the new line,  -3:44 but it could be a comma if you had a string -3:47 with a bunch of comma separating them,  -3:49 and you want to just the individual words you can say comma  -3:52 but words separated by new lines so we are going to say new line  -3:56 and what we'll get back is a list that contains just the elements of the string,  -4:00 that are separated by these items,  -4:02 so we'll call this parts like this, and then we are going to return parts of 0.  -4:07 This is assuming that the shape of this thing is correct -4:10 it might not be good if it's not- anyway,  -4:12 we are going to return this and one more time we might want to  -4:16 strip off the characters on here because there is still maybe  -4:18 some white space in between.  -4:21 Ok, so let's try this one final time,  -4:23 do a little cleanup on the code, run it, all right, go in,  -4:28 and perfect look at that, we have partly cloudy,  -4:33 the temperature is 8.6 degrees,  -4:35 scale is Celsius and the city and state are Portland, Oregon. \ No newline at end of file diff --git a/transcripts/txt/05_app/13_concept_pypi.txt b/transcripts/txt/05_app/13_concept_pypi.txt new file mode 100644 index 0000000..01ddee9 --- /dev/null +++ b/transcripts/txt/05_app/13_concept_pypi.txt @@ -0,0 +1,33 @@ +00:0.14 Let's talk about the two group together paired concepts of accessing these external libraries. +00:6.57 The first is, Where are they? +00:8.06 The second is How do we get them installed? +00:10.05 So PyPi, pypi.org, or the Python Package index, is where these +00:15.93 packages live; these extra libraries live. +00:18.24 And at the time of recording, +00:19.74 there's 267,000 projects. That's 267,000 other libraries that you could choose to install into +00:28.88 your program and start working with them. +00:30.39 Whether you've got to create a little GUI app, +00:32.87 gotta build a web application based on some web framework like Django, +00:36.86 all of those things live here, +00:38.9 and the one that we want is we want to be able to go and make +00:41.93 a web request to an API. +00:44.74 So the other side of the coin is how do we access PyPi and get +00:48.35 the stuff installed and manage them for our application? +00:50.73 Well, that's what this command line tool called "pip". +00:54.34 So we're gonna work with a library called "requests" that lets us do the, well, +00:58.5 HTTP request over to the server to get that JSON document then convert it to +01:2.8 a dictionary to give the weather report, and in order to use it, +01:6.26 it doesn't exist in Python. It's a separate thing. +01:8.59 It works with python, but it's not part of python. +01:11.16 So in order to work with it, +01:12.2 we have to install it into our current environment, +01:15.63 alright? And the way we do that, we would just say "pip install requests", +01:19.07 sometimes you might want to throw in a "--upgrade" in case it's already there, +01:23.54 go ahead and give me the latest version. +01:24.75 But you'll see it when it downloads +01:26.1 it. Actually it already downloaded that version, +01:28.06 so it didn't need to get it, +01:29.02 but you would see a progress bar go across as it downloads if it needs to +01:32.9 then it installs that package and all of its dependencies that it needs, maybe the dependencies +01:38.16 of the dependencies. So everything is ready to run for you, +01:40.47 and it gives you a message like "whatever package you asked for, the current version is installed". diff --git a/transcripts/txt/05_app/14.txt b/transcripts/txt/05_app/14.txt deleted file mode 100644 index 24c6a9b..0000000 --- a/transcripts/txt/05_app/14.txt +++ /dev/null @@ -1,101 +0,0 @@ -
0:00 Let's finish up our program.  -0:02 The last thing we need to do from this get_weather_from_html() function -0:06 is actually return the weather report  -0:08 we are technically printing it here  -0:11 so we are communicating it back to the user  -0:13 but imagine we want to call this function with different data  -0:16 and maybe save it to a database  -0:19 or we get the temperature back for different locations  -0:21 and then we show them on a map,  -0:23 we can't communicate these 4 pieces of information back  -0:27 to the caller of this function, -0:29 that's what we are going to do now.  -0:31 We are going to use this concept called a tuple.  -0:33 There are many data structures we could come up with,  -0:35 we could put these all in the lists and send them back  -0:37 that would work but it's a little too flexible,  -0:40 it's not quite as light weight and constraining as we would hope, right,  -0:44 we want to get these values back and work with them  -0:47 we don't want to append more values and things like that,  -0:49 there is also dictionaries again, maybe little too rich if you will,  -0:54 so tuples you'll see are just right,  -0:56 although there is going to be a challenge to working with them  -1:00 and we'll see that the collections module in Python saves the day. -1:04 So we want to return something from this method,  -1:07 we'll start with return right, what do we put, it's easy to return the temperature,  -1:10 it's easy to return the condition,  -1:13 but how do I return the temperature and condition,  -1:15 I can only return one thing form this function.  -1:17 Well, it turns out, in Python you can define this thing called a tuple.  -1:21 let's go look at that really quick up here.  -1:23 So, in Python we can define a tuple by just putting some values together,  -1:26 they don't have to be the same type, they can be like 1, 7, the word cat,  -1:31 and even some kind of complex data structure like a list of 1, 2, 3,  -1:35 that would be an item in our tuple.  -1:38 Now if we just print out what this is here it should look  -1:41 almost exactly like what I have written.  -1:43 So here is the 1, 7, cat and so on,  -1:46 and we can get individual items if I wanted the second item,  -1:49 so the 7 these are all zero based, so 1 would be the second item,  -1:54 and I can access it like this, we can even do cool things  -1:57 like take a tuple and assign them to a set of variables on one shot,  -2:01 this is called unpacking a tuple so I could have the number 1,  -2:05 the second number I could have the string so we'll say S  -2:08 and then we'll have L for list, if I wanted to assign  -2:10 all the values in the tuple the first one to n1,  -2:13 the second one to n2, the third one to s, the fourth one to L  -2:16 I can just say this and Python will literally unpack those  -2:20 and if I just print out n1, n2, n3, S, L  -2:23 you'll see that actually unpacked those right,  -2:27 this is not the tuples it's just one after another printing them out.  -2:30 The other thing to notice is these parenthesis here are actually optional,  -2:34 even though they kind of help point out hey this is the tuple,  -2:37 it's not the parenthesis that define the tuple, it's actually the commas.  -2:42 So here we have this thing separated by a bunch of commas,  -2:45 and sometimes you'll see weird code like this where you have just 1 item -2:50 but you want it as a tuple so you can sort of treat it uniformly,  -2:54 so I could say 1, and that would be the tuple right there  -2:58 it crashed because I later tried to get the second item,  -3:00 so when you see weird code like that, it's all about the tuples.  -3:03 Now, with that in mind, let's go finish up this app.  -3:06 Back in the weather function, we want to return the condition,  -3:10 the temperature, the scale and the location,  -3:12 and the format here is almost exactly what we want to do  -3:15 so we can return a tuple like this,  -3:18 and PyCharm has little squiggles on here to say you know,  -3:20 those parenthesis- not really needed, ok,  -3:24 so we are going to call this up here let's go to where it's used at the top  -3:29 and we are going to capture the report here,  -3:32 make sure we get the indentation right,  -3:34 now suppose I would like to get the temperature.  -3:38 So for a minute, they get my what would you type here  -3:41 so I am going to say the temp in this location is  -3:45 and I want to do a string.format and I would access the value  -3:48 from the report by saying some index between 0 and 3  -3:52 because we know there is 4 items,  -3:54 which one is it, I don't know, let's see I think it was the second one,  -3:59 let's try just take a guess right, yes, I was right,  -4:03 but it's not at all obvious what to put there, -4:09 there is nothing about this data structure that will help me understand  -4:12 that the second one is the temperature  -4:14 and the only way to know really is to go down here  -4:17 and actually look at the return value explicitly  -4:20 and say oh ok, it's the second one.  -4:22 And, that makes this hard to work with -4:25 and especially if I am looking up here at this code  -4:27 suppose we want to make this a little more interesting.  -4:30 Say the temperature in is let's do the report like it's overcast or sunny,  -4:37 and we'll say 27 degrees Fahrenheit.  -4:41 So I am going to have to write something like this here,  -4:44 so what numbers do I put, well, we know the temperature  -4:47 which goes here is going to be this one so this one is right  -4:50 I think it was condition was the 0,  -4:54 this one let's say is the third and that must be the second,  -4:59 is this right, who knows right, this is super hard to work with,  -5:02 let's try, maybe we get super lucky.  -5:06 The temperature is Celsius, the temperature is in Celsius  -5:10 it's mostly cloudy and 8.4 in Portland Oregon, -5:13 that is not a super sentence, right,  -5:16 but how am I suppose to know from this data structure, right,  -5:19 working with tuples they are great to bundle up sets of related data  -5:23 but they are kind of hard to work with so we can do better. \ No newline at end of file diff --git a/transcripts/txt/05_app/14_virtual_environments.txt b/transcripts/txt/05_app/14_virtual_environments.txt new file mode 100644 index 0000000..0ee5283 --- /dev/null +++ b/transcripts/txt/05_app/14_virtual_environments.txt @@ -0,0 +1,107 @@ +00:0.24 Now we're going to use one of these libraries from PyPi, one of these external +00:3.69 libraries, and we're going to use pip to install it, +00:6.39 but that means we need to address one more concept. +00:10.44 If we install this library into our global operating system environment, +00:15.04 we could be setting ourselves up for big trouble. +00:17.81 What would that be? Well, imagine +00:19.84 application one depends on an older version of a library. +00:23.54 Over time, Somehow that's changed in a way that's incompatible to the program, +00:27.77 but you want to use a new application, build a new application, that uses the new version +00:32.85 of that library. So one app needs the old version of the API, +00:37.42 the newer app must have the new version of +00:40.69 the API. How could those both exist at the same time? +00:43.83 Well, if you just pip install your library, +00:46.72 it's not going to be possible. +00:48.11 You could install one and then uninstall it, +00:49.79 and so the other, and that's not how we should do things. +00:52.1 So let's open the terminal here, +00:54.24 and we're gonna go over and go to my desktop, +00:57.14 make a directory just called "test". +00:59.34 Now, remember if I "pip install requests", +01:2.22 whatever version it gets that's gonna be controlling what application libraries are available for every app. +01:8.44 We don't want that. So what we can instead do is we can create +01:11.99 what's called a virtual environment. So the way we do that +01:15.0 is we say "python -m venv". +01:18.05 So have Python run the module library within itself, +01:21.94 called "venv" for virtual environment, +01:24.64 and I'm gonna put it into a directory called "venv". Those air a +01:27.79 little bit weird, confusing, but it's a convention that's used all over the place, +01:31.55 so I don't wanna stray from it. +01:33.64 Now, we want to run Python 3. +01:36.54 There we go. So now if we look in here, +01:39.93 you can see that there's a bin folder and in the bin folder there's an activate +01:43.5 script. So right now if I ask which python, on Windows you would ask "where python", +01:47.93 it's the System one, okay? +01:50.53 But if I say "dot", this dot means apply to the current shell rather than run in +01:56.39 a new shell, I must say ". venv/bin/activate". +02:1.54 Now notice my prompt changes and I say "which python", +02:6.84 now it's this one, okay. +02:8.86 It's this current one. If I say "pip list" and ask what things are installed +02:13.72 for this python, nothing. If I run Python and I try to import requests, +02:17.55 it'll say "Nope, we only have Pip and setup tools as our external libraries, +02:23.12 nothing else". So if we get out of here, now +02:25.85 we can "pip install requests", installs locally along with its dependencies, +02:35.04 and here we have those and we can now run Python, import requests, and +02:39.93 we could even do things like "requests dot +02:42.06 get" to Google or wherever. +02:46.36 And if we could print out, +02:47.98 you could say the response is equal to that last thing that came out. +02:51.24 Here's the text that apparently is Google. +02:53.09 You can see at the bottom has got some Googley JavaScript, looks like quite +02:56.92 a mess, but nonetheless, we went and got that off the Internet by installing it. +02:59.95 So here's the deal, this application now has this isolated local virtual environment, +03:6.18 and when we upgrade or downgrade or install things, +03:10.23 it's just this list. So, +03:12.17 for example, I can type "deactivate" to get out of this virtual environment, +03:15.6 and if I type "pip list" now, +03:16.88 it just shows me all the random stuff in my global python. +03:21.24 We can use these virtual environments which we create by saying python, or +03:26.72 python3, depending how you run your python, -m venv +03:30.06 venv and it creates it and off we go. +03:33.25 So we want to do the same thing for this application. +03:36.16 So right now you can actually see what version of python we're on, 3.9 down here, +03:40.61 and if you click "interpreter settings", +03:42.59 it will show you that it's the global one. +03:45.25 This is, you know, user local, +03:46.66 this is global one. We don't want that. +03:49.04 So what we can do is either through over here we can click, +03:52.39 "add" or that's what you were actually after you would just go over here, +03:55.77 say "add interpreter" and then what we can do is we can pick a location. +03:59.38 So here we've got the base thing, +04:1.57 the thing we're going to run with the python +04:4.38 -m venv, and then we want to go to +04:7.06 our project directory. Convention is to go to the folder at the top level of your project +04:12.92 and there create a folder, the virtual environment, called "venv". +04:18.64 Now, PyCharm doesn't help us much this time, +04:21.23 but we just type the +04:23.06 "venv" on the end, +04:24.33 we hit ok, it's gonna create the virtual environment for us. +04:28.14 Then we come down to the terminal and notice once we open a fresh copy of it, +04:31.84 it has this venv +04:33.32 and if we ask "which python" on Windows you have to ask "where", notice it's the right +04:37.32 one. It's out of our virtual environment. +04:38.9 And if we ask, "what's installed", shows us that nothing is installed. Again, +04:43.14 we could come over here, we click this "interpreter settings" that would show us again +04:46.31 nothing is installed. We could actually install requests over here like this, +04:51.96 and we could just click "install package", +04:53.94 but I'll show you an even simpler and shorter way. If we go over here and +04:57.17 just try to use "import requests", +05:3.04 PyCharm is going to say there's a problem, +05:4.84 this is not going to run. And if we try to run it, +05:6.68 you can see "no module named requests". +05:8.49 But check this out. If we put the cursor on the squiggly bits and hit +05:11.33 "alt + Enter" it says "Do you want to install this package?" +05:15.23 Why, sure! Please do! notice at the bottom? +05:17.98 It just installed requests. And now, +05:20.98 if we run it again, hey, +05:22.98 look at that. It works! And we go back to our interpreter settings, +05:26.04 now requests is installed right there. +05:28.38 Cool. So that's how we can manage virtue environments from within PyCharm, +05:31.67 but you already saw that you could just use the terminal or the command prompt to +05:36.05 do them directly. What I found was in the early days, +05:39.07 I would do most of my stuff in PyCharm because it helped a lot, but as +05:42.51 I got better and better, I didn't. +05:43.84 There was just little things like I wish it worked slightly different, +05:46.36 so I'm gonna use my own way of doing that. +05:48.94 So now I don't use PyCharm very often to create the virtual environment, +05:51.85 but I did find it super helpful when I was getting started. diff --git a/transcripts/txt/05_app/15.txt b/transcripts/txt/05_app/15.txt deleted file mode 100644 index ee65a77..0000000 --- a/transcripts/txt/05_app/15.txt +++ /dev/null @@ -1,54 +0,0 @@ -
0:00 We saw these tuples, it works but they are hard to use, right,  -0:04 like we need to know what order these items are put in there,  -0:08 and the only way to know really is to go down here  -0:12 and actually look what the return type is.  -0:15 So let's do something better,  -0:17 let's go over here and start with our collections module  -0:20 and when we import collections in there  -0:22 there is a type called named tuples  -0:24 and we can create whatever named tuple we want,  -0:28 and it will be the tuple that knows how weather reports look,  -0:31 so I'll call this a WeatherReport, and just make this part up, -0:36 I'll say this weather report is a collections.namedtuple()  -0:40 and its name is going to be this and then the next item that we put in here  -0:43 is actually a comma separate list of the names of the values,  -0:47 so let's say it's going to have a condition, it's going to have a temp -0:52 it's going to have a scale, Celsius and Fahrenheit -0:56 and it's going to have a location.  -0:58 All right, now this weather report actually defines a type  -1:01 and we can create instances of this type down here  -1:04 so we go back under this get weather report and let's say  -1:07 ok that was cool that sort of worked but it was not the best,  -1:10 and so here we can say report equals and we'll just create a weather report  -1:14 and in here we'll pass the values,  -1:17 if we have the values exactly the same order that they are listed above,  -1:21 we could just copy them in like this.  -1:25 All right, so maybe I remember that right, -1:28 but there is a little safer way to do this so it's more obvious -1:31 and this is using keyword parameters  -1:33 so I can say the condition or the cond=condition and the temp=temp scale,  -1:42 I guess I didn't do a great job choosing different names, the location=loc.  -1:46 so now we've got our report and we are going to return our report,  -1:49 so if I run this code again, I will probably get different output,  -1:53 because of the order, but we'll see that these are compatible with standard tuples,  -1:58 so if I run this again, great, we still get the same meaningless report.  -2:04 But now that we have named tuples,  -2:06 we are in a much better place, let's go fix this,  -2:09 up here we wanted to print out this data  -2:11 and we are using these indexes and we still can but check this out,  -2:14 if we go . we now have a temperature a condition, a location, and a scale,  -2:19 PyCharm knows that named tuples are special  -2:22 and it actually looks in here to help us and get some intellisense -2:25 but regardless of PyCharm, Python will let us say things like .location.  -2:32 So the temperature and the location is temp -2:35 let's see my little message doesn't make a lot of sense here -2:40 let's put temp and then scale say that scale and finally -2:45 we'll put condition here like sunny or rainy and so on.  -2:49 All right, so we are going to our get weather from html  -2:51 we are instantiating one of these named tuples and we are passing it back -2:57 grabbing into report variable and then because it's a named tuple -3:00 we can work with well with the names. Let's try this.  -3:04 That is beautiful, the temp in Portland Oregon is 8.6 degrees Celsius and mostly cloudy -3:12 all right, so that pretty much wraps up our weather app -3:14 let's come back and look at this tuple concept a little deeper -3:17 it's one of our core concepts. \ No newline at end of file diff --git a/transcripts/txt/05_app/15_concept_virtual_environments.txt b/transcripts/txt/05_app/15_concept_virtual_environments.txt new file mode 100644 index 0000000..1b831d6 --- /dev/null +++ b/transcripts/txt/05_app/15_concept_virtual_environments.txt @@ -0,0 +1,76 @@ +00:0.04 Let's talk about virtual environments one more time, +00:1.96 and I specifically want to cover how the Windows version is different, +00:6.39 specifically different than doing things on Mac and Linux. +00:9.71 They're both great. They both have the same concept in the same functionality, +00:13.19 but because of the way the shells work, mostly, and the way that Python gets installed, +00:17.46 partly, you gotta issue slightly different commands. +00:20.94 They're very easy once you get used to them, +00:22.45 but at first it can be a stumbling block. +00:24.63 Okay, so let's review the Linux and Mac OS version. +00:28.45 So what we're gonna do is we want to create a virtual environment in our top +00:31.89 level project, the top directory, +00:34.39 the root level directory of our projects. +00:35.88 Sp we say "python3 -m venv", +00:38.68 so the "-m venv" means run the command within Python 3, and then the +00:42.27 orange "venv" is the directory name. +00:44.33 So those happen to be the same by convention +00:46.62 most of the time, they don't have to be. +00:48.64 Once that happens, it's created, +00:50.42 but you don't actually have access to it yet. +00:52.81 You have to activate it. So you have to say "dot", meaning apply to this shell, +00:56.99 or source, and then "venv/bin/activate". +01:0.04 That's going to change things about +01:2.74 Python, it's going to change the path, +01:5.36 it's going to change the prompt, +01:6.79 and when you run things like "pip List", +01:8.39 you'll see you no longer have the entire system, +01:11.36 whatever is installed for the Python there. +01:13.18 You just have a fresh, empty Python, +01:15.75 and the only packages it has are the tools that you need in order to install +01:20.04 more packages. Pip and setuptools. +01:22.15 And then we can ask "which python" and it says +01:24.64 "It's the one here in this project". +01:26.95 So it's header's a project directory called "weather_app", +01:29.64 You'd see it's the one in "venv/bin/python" and then off you go +01:33.77 to install various things. One thing you might consider, didn't happen this time because Python +01:38.82 3.9 was just released, but often what happens is pip itself is out of date +01:42.92 and any time you try to use pip, +01:44.84 it'll complain to you that it's out of date. +01:46.55 This is like 80 - 90 percent of the time you're pip is out of date. +01:51.08 It's not worth going into why that is, +01:52.31 but when you create a virtual environment, +01:53.82 just get in the habit of always upgrading your base level tools, +01:58.04 pip and setuptools, so you're working with the latest one as you install other +02:1.54 things. So here's what you do on Mac OS and Linux. +02:4.74 This two parts right there are mostly, it's basically where it varies +02:8.85 for Linux versus Windows. Sometimes the python 3 versus python varies, +02:14.84 but that depends on how you set up your Windows machine. +02:16.95 Speaking of windows, over here, same thing. +02:19.3 Probably you just run "python -m", depends. +02:22.79 Then, once we create our virtual environment, +02:25.08 we don't activate it with the dot, We omit the dot and instead of the +02:28.88 bin folder, there's a scripts folder. +02:31.08 So you say "venv\scripts\activate" why there's a difference there, +02:34.71 they haven't unified those things, I have no idea because it seems trivial to do +02:38.99 so, but they haven't done it for years and years, +02:41.43 So this is how you do it in +02:42.23 Windows. Just remember, not bin, scripts, +02:44.52 pip list, exactly the same. +02:46.63 Once you've activated it, your prompt changes, +02:48.52 it has the venv +02:49.44 in there again. Now you can't ask which on Windows, +02:52.39 That's not a command, but a command that does work on command prompt is "where". +02:56.56 So here you get the same basic answer. +02:59.34 It's the version of python in the virtual environment Windows style. +03:2.19 So scripts folder, python.exe, +03:4.21 you'll also see other pythons. But when you type python, +03:7.01 it's going to show, the where command shows +03:8.56 you all of them in the path, you're going to run the first one that's +03:11.75 found in the path, which is the blue one from the virtual environment. +03:14.97 Again, you want to upgrade pip and setuptools for exactly the same reason. +03:18.43 So the big difference, these two. Remember, +03:20.91 it's no dot, scripts not bin, +03:23.5 and then "where" instead of "which" Python. That's it, +03:26.64 they might seem like a bit of a stumbling block it first, +03:28.62 but trust me, once you get used to them, +03:30.44 you just do it the way that you do it on any OS and off you go. diff --git a/transcripts/txt/05_app/16.txt b/transcripts/txt/05_app/16.txt deleted file mode 100644 index f982df8..0000000 --- a/transcripts/txt/05_app/16.txt +++ /dev/null @@ -1,54 +0,0 @@ -
0:00 Let's talk about tuples -0:01 these are one of our core concepts from this application,  -0:04 we saw that tuples aggregate or bind together  -0:07 related sets of data but that data doesn't have to be all the same type,  -0:11 here we have three numbers and the string -0:14 and this is going to represent some kind of measurement -0:17 now here we are going to put the temperature  -0:19 and we've used the convention and that's really all it is -0:22 it's a convention that the temperature is the first value  -0:26 and the measurement quality is going to be the fourth item -0:28 so the third index, again, just a convention.  -0:31 Now, it's really the commas we saw that help define a tuple -0:35 it's not the parenthesis even though they look  -0:38 kind of like there are key part to it -0:40 kind of here this line is equivalent to the one right at the top -0:43 when you print it out regardless of whether you put the parenthesis  -0:45 Python print it with the parenthesis,  -0:47 the other thing we saw is that you can unpack tuples into individual variables -0:51 so here we have a tuple m which looks like this  -0:55 and we have four individual variables we want one that holds a temperature -0:58 one that holds the latitude, the longitude and finally the quality,  -1:01 we can just reverse the assignment  -1:03 so the bunch of variables equals the tuple and Python will unpack it.  -1:06 Here you can see the values that get put into the individual variables.  -1:11 We saw that tuples are really helpful in quickly binding together  -1:14 related sets of data that you can pass around as a single item -1:18 however, consuming them was not so easy -1:22 you just index into them and you have to know  -1:24 hey, the fourth thing is the quality so always use bracket 3 to get hold of it -1:29 And that's where named tuples come in -1:31 so if we import the collections module, we can say collections.named tuple  -1:35 and we can define a type so here is the measurement, -1:38 this thing doesn't have values, it's a template to generate tuples  -1:42 that do have values but conforming to a particular structure  -1:45 where the first thing is the temperature,  -1:47 the second thing is latitude, third longitude and fourth the quality.  -1:51 Here we create an instance of our measurement class,  -1:53 passing the four values, this works because the orders are lined up just right -1:56 we also saw we can use key value pairs and say like  -1:59 temp=22.5 quality=strong and then the order doesn't matter -2:04 and then we can treat them just like tuples -2:06 here we can get the temperature and say bracket 0 -2:08 but the real power is that they now have attributes  -2:11 we can say m. and then the names that we put here  -2:14 representing the positions so m.temp is temperature -2:17 m.quality is quality, when you print it out it's a little more obvious -2:20 like hey this is a measurement  -2:22 and the various values correspond to temperature 22.5 and so on -2:26 that's just Python's formatting.  -2:28 So my recommendation to you is when you are thinking about using a tuple -2:31 and you care about getting the values back out, and when wouldn't you,  -2:35 very well may make sense to create a named tuple  -2:38 to help you and all the other people that work with your code later on  -2:42 be more successful with it. \ No newline at end of file diff --git a/transcripts/txt/05_app/16_calling_the_api.txt b/transcripts/txt/05_app/16_calling_the_api.txt new file mode 100644 index 0000000..f0bf3f2 --- /dev/null +++ b/transcripts/txt/05_app/16_calling_the_api.txt @@ -0,0 +1,60 @@ +00:0.14 It might seem like we're a long way from being done with this application. +00:3.18 We've talked a lot about some honestly slightly complicated topics with virtual environments and dependencies and +00:9.05 manage them and all that kind of stuff. +00:10.81 But we're actually extremely close to being done, +00:13.5 as you will see. So let's go over here to the "call_weather_api". +00:15.66 I've have already written "import requests" at the top, so we can jump down here. +00:19.84 Remember, we've already got the location as a named tuple, +00:23.44 and we're using it to put together the URL. +00:26.14 So here we just said, we would call the URL. +00:28.47 Well, let's not say we would, let's do call it. +00:31.39 So we're gonna get a response from the server. +00:33.38 The way it works is you go to request you say, +00:35.06 "get the URL", done. We've now downloaded the data from the Internet. +00:39.4 How insane is that? So let's just print out what we got. +00:42.74 If I say Portland like that, response 200. 200 in the Web is okay, +00:50.64 success. So that's good. There's all kinds of things here. +00:53.46 One of them is the text. +00:55.06 So if we print out the text from Portland, what do we see? +00:59.19 Oh oh oh.. broken clouds. How about that? +01:2.43 Super cool. Now we could use the libraries that understand strings and convert them to +01:8.75 Python dictionaries by parsing the JSON document, +01:12.48 but requests already knows how to do that. +01:14.95 It has a function called JSON. +01:17.94 So if we write that and we type "Portland", +01:20.44 it looks the same, Right? +01:21.57 But remember, Pythons representation of dictionaries has single quotes. +01:24.85 Our Web server's returning double quotes. +01:26.78 So what actually came back is just data and it just when you print it out +01:29.99 it looks basically the same. +01:31.41 So check this out. We'll say "data equals response +01:34.38 dot JSON". Now, Before we go through that step, +01:37.05 we got to make sure that we actually got the right values. +01:39.81 So what we need to do is say if for some reason we got something other than +01:44.74 a 200 ok request and JSON response, +01:47.56 we just want to say "sorry we couldn't get you any weather". +01:49.89 So the easiest way for us to do that is to ask if the "response dot +01:54.64 status code", that's that number, is in a set of things. We could do a +02:0.06 bunch of if statements or we could just say, +02:1.83 "Is it in 400?" which would mean bad request, 404 +02:5.86 like if you asked for a city that doesn't exist, +02:7.94 500, the server crashed. We could return "None". +02:12.74 In fact, we could say, +02:14.57 basically, it's not 200, return an error, +02:17.19 right? So here we'll print out "error", +02:19.58 and then let's just put the "response dot text". +02:23.21 A lot of times when there's an error, +02:24.8 you actually get an additional message in the text, +02:27.16 so this might help us debug what's going on. +02:29.7 Like, for example, if we go and ask for the city such and such, +02:33.01 it says "error, what we got back is 404, city not found". +02:37.48 Okay, so once we make it this far, +02:40.39 we think we're in good shape, +02:41.81 and this might, I'll leave it like this, +02:43.68 but you might just say "not equal to 200". +02:45.45 So the last thing I want to do is I actually want to convert this response +02:50.24 into something that's easier for us to work with. +02:53.1 But we're already off to a pretty good start. +02:56.98 What about Boston? Whoo hoo! Boston! Alright, +03:1.11 we returned it. We haven't printed out anything yet, +03:2.74 but our final step is going to be to convert that into something meaningful we can show to the users. diff --git a/transcripts/txt/05_app/17.txt b/transcripts/txt/05_app/17.txt deleted file mode 100644 index 70bc8b4..0000000 --- a/transcripts/txt/05_app/17.txt +++ /dev/null @@ -1,153 +0,0 @@ -
0:00 Before we move on from our weather app -0:02 let's look at one other important concept around PyPi, packaging, pip -0:08 and this whole external package management for Python.  -0:12 Remember, we could see what was installed by saying pip 3 list -0:16 and over here we have various things like stripe -0:19 1.28.0 for credit card processing.  -0:23 And we have pigments for web apps  -0:26 now what do you do when you have  -0:29 more than one application you want to run on your machine.  -0:32 And they both use stripe but the stripe API had  -0:36 a breaking change from 1.26 to 1.28  -0:40 and one of the apps is written to use the 2.6 API  -0:44 and one of your apps is using the 2.8 API -0:46 what do you do, do you just reinstall and uninstall  -0:49 and push the versions backwards and forward -0:52 like how do you deal with that version difference, right -0:54 you can't run both apps on your machine at the same time -0:57 without continuously reconfiguring your machine -1:01 also, if I was going to give my app to somebody deploy to production -1:06 give it to a user maybe runs on their machine -1:09 how would I know what I need?  -1:11 In this list, what is in this list is required to run the weather app?  -1:15 Well, you and I know because we just wrote it -1:17 we know that requests is needed and up at the top bs4 is needed. -1:22 But, there is nothing here that makes it clear what is needed -1:27 now there is ways to do that and with packaging -1:29 and packaging up your apps in certain ways -1:32 but the environment itself does not help us here  -1:34 so we have these two problems -1:36 different apps may use different versions of the same package  -1:39 and it's very hard to tell what's needed to run a particular app.  -1:42 Another problem is if I want to install something here  -1:45 I may need to run this as admin, also not the best.  -1:49 So there is a way to solve this problem  -1:52 and there is an external package that will solve it for all versions of Python -1:55 there is a built in version in Python 3  -1:59 that only works in Python 3 called Venv -2:00 and I'll go and show you the way that works  -2:04 uniformly across all the different versions.  -2:06 So there is something called virtual env that is built to address this problem,  -2:10 let's see how we can use virtual env to create a special dedicated environment -2:14 completely isolated from the general machine  -2:16 just for this weather app and its packages.  -2:19 I made a folder called Python environments,  -2:21 let's go in there and see right now it's empty,  -2:24 so I am going to use virtual env to actually create environment,  -2:27 so one question you might want to ask is which virtual env do you have.  -2:31 We have the one from Python 3 so you want to make sure you run that one,  -2:35 in this case we need to type virtual env as a program,  -2:37 if you want to run the other one  -2:40 you might have to run it as a module and through Python directly -2:42 so it's something like Python 3-env space virtualenv.  -2:49 Like so, but we don't have to do this,  -2:51 we can just type virtualenv, right,  -2:55 so what we are going to do now is we want to tell virtual env go and create  -2:58 a clean empty Python environment, Python 3, for us to work with.  -3:02 So we'll say virtualenv and then we'll just give it a folder name -3:05 so let's call this weather. py3  -3:08 just to make it more obvious this is for the weather app and it's Python 3.  -3:13 You can see that it's copy the Python 3 executable over and it's also setup pip -3:17 setup tools and few other things.  -3:20 So now if I look there should be this folder, if I type correctly,  -3:22 there will be this folder, so we can go in there and look and there is a bin folder -3:26 so let's go into this bin, but before I go into the bin  -3:31 let me ask the question which Python 3 would I get if I run it -3:35 and let's ask pip3 list.  -3:38 There is all the stuff that's in the main machine, ok, -3:41 so let's look in we go in the bin -3:43 you'll see that there is an activate -3:46 and what we are going to do is we are going to run this activate  -3:48 and we want to run it against the current shells, so we'll say -3:50  . to source activated -3:52 this isn't necessary in Windows, the dot, it doesn't work, I don't believe,  -3:55 we'll say this now watch the command prompt change -3:58 now you see it says weather 3.  -4:00 And if I go somewhere else, you see now I know that I have  -4:03 my Python 3 weather environment as the active Python environment  -4:07 so if I ask questions like which Python -4:09 oh now all of the sudden I've got the one running out of there  -4:12 regardless of where I am and if I say pip list -4:15 well we just have an empty system.  -4:18 Now the version that got installed into our little virtual environment is out of date -4:21 let's just not worry about that for now.  -4:24 Ok, so we can actually use this to run our app.  -4:28 So let's go over to where we wrote our weather app say CD and this, -4:34 if we look in here we can see there is our program, that looks familiar, right,  -4:38 this is our little weather app, great,  -4:41 but if we try to run it we say Python 3 but we don't need to say Python 3  -4:44 now there is only one Python in this virtual environment -4:47 and we give it our program, there is going to be a problem.  -4:51 It says, this concept of Requests as a module-  -4:54 this doesn't mean anything to me -4:56 there is no module named requests  -4:59 because in this environment we have nothing -5:02 we just have this clean environment here -5:04 so let's quickly install what we need, we know that we need  -5:06 Requests and we need Beautiful Soup 4.  -5:10 We can install them both at the same time like this,  -5:14 if we don't forget to put the install command in there.  -5:20 Excellent, now if we ask pip list you'll see we have  -5:23 Request, Beautiful Soup and nothing else -5:26 I am kind of getting tired of this, so let's just run it  -5:28 so we have something nice and clean.  -5:31 So now if we list, you can see we just have Beautiful Soup, Request  -5:34 and the 3 foundational bits.  -5:36 Ok, so again, let's try to run this -5:38 we'll say Python and we'll give it the program -5:41 oh look, it's running, all right, 97201 perfect -5:46 the temperature in Portland, Oregon is 7.4 degrees Celsius  -5:49 and it's partly cloudy, how awesome is that, -5:52 so it doesn't matter if somebody else installs  -5:55 Requests or Beautiful Soup on this machine  -5:57 and they have a different version or they updated -6:00 we are a 100% isolated and just have this working version of Python here -6:05 this clean version of Python 3  -6:10 and our own user profile in this folder that we created.  -6:14 So the last thing to do is use this, in PyCharm.  -6:17 So we are over here in PyCharm  -6:20 and if you see when I run it that we are still running out of the main python 3.5  -6:24 and when I go to the project interpreter,  -6:27 you can see that's one we have selected here  -6:30 and we have again all the stuff that we saw in Pip list, -6:32 we can actually go over here and select one -6:35 if I hit show all it lets you have this little management thing -6:37 here is an old one I can clean up, then I can add,  -6:40 I can say add local or I could even create  -6:43 a virtual environment that a whole process you just saw me do with virtual env -6:47 creating this stuff and all that, just click this button and it will actually do that for you.  -6:51 So that's super nice,  -6:55 but what I want to do is add the one that we just created.  -6:58 So we can just browse over here to our environment,  -7:01 and it loads up and we just pick Python,  -7:06 perfect so now we have this, we can actually select this  -7:09 to be the project interpreter for the current project  -7:12 and now look what's in here, you can see it's just that clean environment,  -7:15 so let's go over here hit ok, it takes a moment for it to update and index the -7:20 environment, now it's ready to run, now look at this, -7:23 user/screencaster/python_environments/weather_py3bin/python,  -7:28 so now we are using again form PyCharm  -7:31 that isolated clean environment, very nice,  -7:33 and let's just check one final time what the weather is in  -7:36 let's say 92118- beautiful, the temperature -7:39 in lovely Coronado California is 15.2 degrees Celsius and mostly cloudy,  -7:44 hey, where is the sun San Diego? -7:46 Anyway, this is working using our environments,  -7:49 using our packages that we have installed form PyPi using pip  -7:52 into that environment- lovely. -7:55 So that brings this application to a close  -7:57 I hope you've learned a lot and had a lot of fun doing it,  -8:00 we are just going to re-iterate one more time,  -8:02 remember, you should try to use APIs and verify  -8:06 the usage rights when you are doing screen scraping  -8:09 but it is a really cool way to get data into your app. \ No newline at end of file diff --git a/transcripts/txt/05_app/17_converting_api_data_to_weather.txt b/transcripts/txt/05_app/17_converting_api_data_to_weather.txt new file mode 100644 index 0000000..038dff6 --- /dev/null +++ b/transcripts/txt/05_app/17_converting_api_data_to_weather.txt @@ -0,0 +1,85 @@ +00:0.04 Recall what we got back from here with our data. +00:3.26 Let's just print out Portland. We got all of this stuff, like our weather with our +00:7.34 details and our categories and whatnot, what I want to do is get more structured, +00:11.93 simple data that we could work with. +00:14.07 We did that up here with locations, +00:15.92 and let's just actually do the same thing for weather. +00:18.02 So we'll say whether or weather report or something like that is gonna be another named +00:21.77 tuple and it's just gonna have city, +00:26.07 let's actually, you know, we could have city, +00:27.85 state, country, or we could pass over just a location object. +00:30.78 So let's say it just has a location and then it has units. +00:34.62 Is it metric or is it imperial or whatever? +00:37.39 And then the temperature and the condition. So if it has +00:40.16 those things, we'll be able to +00:42.04 create it down here. So all we gotta do is come down and, it's not, we +00:46.04 can print it for a minute. +00:47.64 I'm gonna go over here and what we need to pass is the location which is already +00:51.82 passed in, so that's easy. +00:53.44 The units, well, the units we're passing over are right there, so you could pass it +00:59.6 however you want. And then we need the temp and the condition. +01:4.24 I'm gonna do a short little version here, +01:6.19 and then we'll do an improvement in a minute, +01:8.49 but let's return the weather. So we'll say the temp is data, +01:13.65 now remember, how do we get this out? +01:15.38 It actually is kind of all over the place. +01:17.68 We want to run. Remember, +01:19.9 there's weather, which has, like, +01:22.64 the description here, so that's gonna be interesting, +01:24.97 I'll put that up here for us. The other part that we care about is what's called +01:33.49 the forecast right here, it's wrapping around oddly but like this. +01:40.44 So what we actually need to do is we need to get, go to the weather +01:43.54 and we're going to get some stuff here and then go to the forecast and then +01:46.78 get the temperature, so the temperature is gonna be go here. +01:49.87 I'm going to get the forecast and from the forecast, +01:53.66 we're going to get the temp, and then the condition, +01:59.64 same type of thing. Get the weather, then we want to get like we could +02:5.96 get the description right. But I want to say, "category : description", +02:10.38 So what we could actually do is come over here, like thi, and say we're gonna +02:13.57 put this into a little variable, and I could just say, create an f-string, say +02:19.01 it's gonna be "w dot get category" +02:23.44 and then outside the curly braces "colon w dot get description". now, +02:29.21 the description I wanted to be a capital first letter. +02:32.08 So a cool way to do that is to say, +02:34.02 "capitalize", makes it like a sort of like a proper sentence. +02:38.34 And then here put a period on the end. +02:41.74 So let's just print out the weather before we go any farther here, +02:45.56 see what happens. If I say "Boston" got a bunch of stuff printed there. +02:50.98 But this part, this is the interesting part. +02:53.06 Look how cool that is! So we got the location +02:55.89 is Boston, bo state specified, US. +02:59.1 Units are imperial, temperature is +03:0.56 53 degrees, and the condition is clouds, +03:3.55 overcast clouds. Super, super cool. +03:6.67 So the final, final thing we've got to do is just show that to the user +03:10.81 in some meaningful way. But this is starting to look complicated, +03:13.91 and it shouldn't be complicated. There's not much going on here. +03:16.44 All we need to do really is we wanna actually, +03:19.67 maybe even do a little bit more like so when it said that the temperature was +03:22.67 59, we're not using the units either, +03:25.73 are we? So what I want to do for the unit is we're gonna need +03:29.84 to get the, whether or not it's imperial or metric. +03:32.82 But you don't say the degrees or imperial, +03:34.66 you say they're Celsius or Fahrenheit, +03:36.84 so we're gonna have to do a little conversion there. +03:38.39 And what I want to do is I wanna make this something that we can put +03:41.29 away and make it simpler. So let's say, we're going to convert this data over +03:46.58 to a weather report so we could go and +03:48.35 write a function, I could come down here and type "def convert +03:52.61 API to weather object" +03:57.17 right? Or PyCharm actually has our back here, +04:0.1 which is pretty cool. We can go and +04:1.6 highlight that, right click, refactor, extract method, and you give it a name, hit +04:8.32 enter, and check that out. It wrote that right there for us. +04:12.21 So that's cool. It's, uh, +04:13.64 if I deselect it here, you can see that it's helping us out here a lot +04:17.36 by cleaning things up. We don't need to print the weather. +04:21.64 We could, in fact, just return this directly. +04:25.54 Now, if we go back to our calling the weather API, +04:27.9 it looks simple again, and that is because it's simple. +04:29.97 It was just getting a little bit complicated there. +04:32.84 So now, call this "weather" maybe, up at the top, +04:36.17 and, print it out one more time, +04:38.48 It's not quite the end, but you can see nice print out of everything. +04:44.38 All we gotta do is convert that into something we'd like to show the users, +04:47.42 maybe get rid of that, and we'll be set with our weather example. diff --git a/transcripts/txt/05_app/18.txt b/transcripts/txt/05_app/18.txt deleted file mode 100644 index ffb4c14..0000000 --- a/transcripts/txt/05_app/18.txt +++ /dev/null @@ -1,20 +0,0 @@ -
0:00 Before we completely step away from our weather app -0:03 let's talk about the final concept virtual environments -0:06 recall, virtual environments allow you to create isolated -0:10 dedicated environments for a particular application -0:13 in there you have a copy of the Python runtime -0:16 and you have initially, basically no packages  -0:20 but you can use pip to install those packages  -0:23 and they won't conflict or battle with any of the machine wide ones -0:28 you just have only what you need in this nice clean environment.  -0:31 It helps you understand how you deploy to production -0:34 it helps you understand what requirements you want to tell other users to have  -0:38 they are going to run your scripts on their machines -0:41 now just creating the virtual environment is not enough -0:44 remember, you have to activate it so you change into the environment folder -0:48 and into bin, and you say .space activate on OS X or Linux  -0:52 and on Windows you just say activate -0:55 I believe it might be a batch file.  -0:57 But, you call activate and then your command prompt changes -0:59 your path changes so that all the Python tools like Python itself -1:03 pip and so on now run exclusively out of that environment. \ No newline at end of file diff --git a/transcripts/txt/05_app/18_user_friendly_report.txt b/transcripts/txt/05_app/18_user_friendly_report.txt new file mode 100644 index 0000000..fcab280 --- /dev/null +++ b/transcripts/txt/05_app/18_user_friendly_report.txt @@ -0,0 +1,131 @@ +00:0.14 The final step here is to report the weather, +00:1.98 but in a nice way that users went to see that they expect. So instead of just printing +00:7.17 out this random data structure, we're gonna go and do a print statement. +00:11.16 So I'm gonna say print, little +00:12.79 f-string, the weather and just put little curly braces there for some for a bit because +00:19.22 we don't have it done yet, in some location, +00:21.41 like Portland, is so many degrees, +00:24.47 whatever the scale, Celsius, Fahrenheit and the condition is cloudy or something along those +00:30.21 lines. Take that away just for a minute +00:32.17 so it doesn't crash. So what we need is to get stuff like the location. +00:36.46 So it would be easy to think we could just put like the location there, +00:42.07 but remember, the location is this data structure, +00:44.32 and it'll look weird. And it will say that the state is None some +00:48.44 of the time. So what we need to do is get some location +00:50.75 text. Let's say location_name, +00:55.04 so get location name and we'll pass the location. +00:59.68 Let's write this little bit here. +01:4.04 And really, it all comes down to whether or not we have the name of +01:7.66 a state or no state. So we'll say, +01:9.12 "if not location.state" +01:11.86 So there is no state specified then we just want to return a string that +01:15.93 says "location dot city comma location dot country". +01:22.05 Now there's a small problem here, +01:23.77 It's gonna be lower case city and lower case country because we converted it that way, +01:27.61 remember? We did all that lower stuff to canonicalize +01:30.48 it to make sure we always sent the lower case thing to the server? +01:33.87 Well, we wanna undo that. +01:35.24 So now we can say "capitalize" here on this one. +01:39.39 And then the country, remembers it was a two digit code which probably just wanna make that upper +01:43.95 case. Now, if it's not the case that there is no state, +01:48.02 so there is a state, we just want to add it in there, +01:50.53 and it's gonna look like this because the state is an abbreviation as well, +01:54.8 so we'll just put state there, +01:56.84 and that's it. So let's just print out, comment that out for just a minute, on +02:1.64 the way, location name. +02:3.84 If I say, let's say Seattle, +02:7.12 Washington, US, The weather in, look at that, +02:10.6 Seattle, Washington, US. fantastic. +02:14.06 Alright, So what else? +02:15.15 What else do we got to do to make this work? +02:16.69 Over here We have It's the temperature. +02:20.24 So we're going to say the temp is easy. +02:23.26 That's just "weather.temp" and the scale, +02:26.43 let's just put it for a minute "f", +02:28.13 we'll fix it in a second, +02:29.84 let's say temp, scale, and then the condition as +02:33.45 well, gonna be "whether.condition". +02:38.23 Now, some of these we could actually just in line them like We don't need +02:40.89 a separate thing for this, I guess, +02:43.17 probably for the temp, either. +02:44.98 But for the scale, we're gonna need some help. +02:47.47 Let's go ahead and run this. See what we get. +02:48.9 Make sure it's working. Boston, the weather in Boston, +02:52.07 United States, is 53°F, clouds and overcast clouds. +02:57.46 And I think in this condition where we created that, +03:2.24 we'd already put a period. I didn't want the period there +03:4.69 I don't think like there. Try one more time. +03:7.76 Boston. Look at that. Perfect. +03:10.35 The weather is 53.29°F. +03:14.14 The last thing that actually make this legit is to determine whether that's an F or +03:18.67 a C. And that's actually one of the easiest things we've done. +03:21.11 So let's go over here and we'll just say "if whether.units equal imperial else +03:35.53 the scale is C". So let's change where we're calling that just to make sure if +03:42.21 we put metric over here, we should get the right answer. +03:46.82 Boston and you spell it right, +03:50.14 Boston, here we go. But are we, we are not getting right here, +03:55.2 are we? Let's see. And I think that's because when we called this, +04:2.0 I hard coded that into two places, +04:3.81 which is never a good idea. +04:5.64 But down here, we just wrote +04:7.79 this right. So I think we wanna put units in here and pass it over. +04:12.14 We don't have to pass it. +04:16.96 It's with the data. So here we just do "data +04:20.43 dot get units". Alright, +04:23.1 try again. Boston. Look at that! See, now if we go and just change this to Imperial +04:32.18 and Boston again, perfect. There it is. +04:35.44 Our little scale is being shown. We can do one more thing on the scale to make +04:39.13 it nicer. It would be better, +04:42.06 I think, if this happened on its own place. +04:44.78 So we can come over here and make a method called "get scale", +04:50.14 something like that. This writes +04:51.83 this little function so we don't have to worry about in-lining that, makes our +04:55.69 code up here much simpler. Okay, +04:57.54 so we're pretty much there. We can trim up some of those comments. +05:0.24 Like, do we need to say show the header? show the header. +05:2.89 No, those basically say the same thing. +05:5.48 Convert Plain text, says the same thing. Call the weather +05:9.0 API. Report the weather. This could even be a function that we create if +05:12.92 we want called "report weather" that might be cool. +05:17.44 Create a method called "report weather". +05:20.84 Here we go. And now how much, +05:24.84 how many comments do we need? We've got show header, +05:26.93 what do you want to do, convert to plain text, call the API, +05:29.71 report it. Super, super clean way to have our program working here. +05:34.74 There's one final piece that we want to take care of to +05:38.59 really put a nice cherry on the top. +05:40.77 Now check this out. If I type Boston and miss the T, Boston, +05:44.71 something crashed. This came back as "None" +05:48.35 and we tried to get the units which made it crash right there in the get +05:51.52 scale. What we need to do is there's a couple places like if they don't +05:57.12 pass the, we were not able to create the location, +06:0.12 right? They passed in nothing or something, we want to print +06:3.54 "Could not find anything about +06:8.24 the location" location, text +06:11.36 let's say. Like that, and then return. +06:15.04 Right. So now if we come in here and we just hit enter, couldn't +06:18.4 find anything about that. There's also the same things happening right here. +06:21.8 So if not whether, if it comes back +06:23.59 as none, we'll say "could not get the weather for this place from the +06:31.67 API". Now, if I put "bos", somewhere +06:35.05 we're still printing that out, right? +06:36.21 But it says could not get the weather for bos from the API, +06:38.59 but if I do "Boston", perfect, +06:42.64 that works. We got one stray print in here before we call it, totally good, +06:49.84 maybe we want to keep it there, +06:51.2 maybe we don't, right probably belong in some kind of logging or, +06:55.23 I don't know, we could get the status code and try to make sense of +06:58.17 that and so on. But I'm just gonna put it like this and call it +07:0.5 done. So even though it sounds like quite a big deal, +07:3.7 what we've done is pretty straightforward, +07:7.14 right? We just do a little bit of string work to get the data in +07:10.04 the right format, we call the weather +07:12.16 API using requests which we've installed into our virtual environment using pip, +07:16.24 and then we just take that dictionary data that came back and put it over and +07:20.39 here, we just call request.get, make sure it was successful, +07:23.96 JSON, and we're done. Beautiful. +07:26.64 This is a really, really cool app, +07:28.28 and I would consider it a real app. +07:30.36 It has really error handling, it has real behaviors, super cool. +07:35.11 Working with live data off of a +07:37.31 live API, gotta love it. diff --git a/transcripts/txt/05_app/2.txt b/transcripts/txt/05_app/2.txt deleted file mode 100644 index 1693fe2..0000000 --- a/transcripts/txt/05_app/2.txt +++ /dev/null @@ -1,75 +0,0 @@ -
0:00 Before we talk about pip and packages  -0:02 and the Python package index and all of those things -0:05 let's just sketch out our app as far as we can take it  -0:08 and then we'll bring in the packages.  -0:10 So we are going to get started as always by writing a program.py file  -0:14 and let's also start this time by defining a main method  -0:18 that's going to be sort of the top level method to be called here -0:21 remember, this is not a Python convention, this is just Michael convention,  -0:25 so we are going to have to do things like -0:26 I'll just sketch out in comments like print the header -0:29 and then the next thing we have to do maybe is get HTML from web -0:35 so I want to download the HTML,  -0:38 then we are going to have to parse, the HTML,  -0:41 we have to display the forecast.  -0:43 And at some point I suppose we are also going to need  -0:46 to actually get the locations, we'll say get zip code from user.  -0:52 Ok, so these are the kind of the steps  -0:53 and you can see there is little warning -0:55 that's because there is actually nothing happening in this method -0:58 let's just do a print hello from main really quick -1:01 now recall, if I run this there is no run configuration  -1:05 so I'll make one by right clicking and say run program- nothing happens,  -1:09 our little print statement doesn't show up  -1:11 and that's because we have to call main() -1:13 and we saw that this really is not the right way to do it -1:16 if somebody wants to reuse this module, this script,  -1:20 if they import it this is always going to run our app,  -1:23 so we saw that there was that convention we say, if__name__='__main__'  -1:31 then we would run our main() method,  -1:34 this is so common, that PyCharm has what is called live templates.  -1:39 So I can come over here and you can see that there is main(),  -1:42 this is the method I wrote  -1:43 but then there is this main which has actually got some code here  -1:47 and if I just hit tab it actually auto expands out of here. -1:51 So this is one of the live templates,  -1:53 I'll pull those up really quick,  -1:55 you can see there is actually a ton of live templates here, -1:58 that you can use, here is the one that we are running  -2:00 but for all sorts of various not as Python  -2:02 but SQL and Django and so on,  -2:04 so let's go ahead and use our main() bit here,  -2:07 and in that case we are going to call main().  -2:09 So now our job... let's verify runs, still is, -2:12 now our job is to actually start writing these methods and drop this bit.  -2:15 So we are going to print the header so we'll just say  -2:18 call a method print_the_header(), or print header however you want to call it, -2:22 and let's put this down below, like so  -2:28 and remember, all of our headers looked something like this,  -2:35 and here it says weather app centered, usually, and the final new line.  -2:41 So perfect, that is solved.  -2:43 The next thing we want to do is get the zip code from the user,  -2:46 so we can say zip, it's not exactly what we want to say  -2:50 but let me just start this way for a moment, -2:52 input will say what zip code do you want the weather for,  -2:59 and let's give him a little hint, and say like 97201 as an example.  -3:04 Now, notice there is a small warning here,  -3:06 we have a little problem with zip, -3:09 there is actually two warnings, one is that we are not using it,  -3:12 but the more important warning,  -3:14 the not using the part is going to go away, -3:15 is that this shadows the built in name called zip, -3:18 zip is a built in function that does kind of map produce like stuff,  -3:23 and this is overwriting, basically hiding the zip function for the rest of this method -3:29 so let's change this, I just want you to be aware  -3:32 that those types of things can happen,  -3:33 so let's change this to code now there is still a warning  -3:35 but it's just that we haven't used it yet, so that's not a big deal.  -3:38 The last part is to get the HTML from the web  -3:41 and we need to go talk about  -3:43 where on the web we are going to get this  -3:45 but let's just do a print out of the code really quick  -3:48 just to make sure everything is hanging together.  -3:50 So what is your zip code, it's 01010 five digits perfect,  -3:54 everything is working fine, except for we now need to go get some HTML off the web.  - diff --git a/transcripts/txt/05_app/3.txt b/transcripts/txt/05_app/3.txt deleted file mode 100644 index 4ca064e..0000000 --- a/transcripts/txt/05_app/3.txt +++ /dev/null @@ -1,71 +0,0 @@ -
0:00 The website that we are going to use for our example  -0:02 is called Weather Underground.  -0:04 Now, Weather Underground is a super cool weather site,  -0:08 there is a bunch of weather sites out there, -0:10 national weather service, things like this  -0:12 but the Weather Underground is kind of different  -0:15 and see, all of these green dots, these are all kind of in my neighborhood  -0:18 where I am in Germany right now. -0:20 These are all personal weather stations that individuals have put at their house  -0:24 and then linked up to weather underground -0:26 and so if you live right here, you can actually get the report from where is that,  -0:31 a few blocks down the street so this is really cool.  -0:34 Now, if we want the weather in say Portland,  -0:36 we can come over here and we can just search for it,  -0:39 or we could just go wunderground.com/weather-forecast/zip code.  -0:45 So, here you can see the local forecast, see if there is some rain,  -0:49 yeah there is some rain out, towards the mountains, the cascade, -0:51 what we are going to do is we are going to go and actually grab this HTML -0:55 and we are going to download it  -0:56 and we are going to pull this element out right here and use that for a report,  -1:00 we are going to pull out this temperature right here the wind,  -1:03 the weather forecast in terms of whether it's rainy or sunny or so on,  -1:08 we are going to take all that, we are going to turn that into something that we can display to the user.  -1:13 Now, one thing I want to say really quickly  -1:15 before we get into this is this concept is called screen scraping,  -1:18 and it may or may not be allowed by various websites -1:21 if you are trying to write an app and you want to basically  -1:24 turn this web pages into an API, you should do two things,  -1:27 you should check the terms of service to see  -1:30 if that particular website disallows that  -1:32 and you should check to see if they have an API,  -1:34 so if you go back here you can go to the bottom,  -1:36 you can see that these guys actually have a rest API that returns json or xml,  -1:43 so if you are going to consume weather form the Weather Underground for real -1:47 please use their API they have a free version, that sort of thing,  -1:49 this is only for demonstrating the sort of pip concepts.  -1:54 So for starters, let's grab this url, and head over to PyCharm.  -2:00 So the next thing we are going to so is get the HTML from the web -2:02 so let's write the method, def get_html_from_web(), -2:09 and we have the url, it's going to be here like so,  -2:12 now obviously we don't want to always get the weather in Portland Oregon,  -2:15 maybe we want to let the user enter it, just like they did up here,  -2:18 so let's pass in the zip code, as a string  -2:21 and then we want to put it in here.  -2:24 And we could kind of do this but I have always kind of preferred  -2:28 the format style so I put a little curly brace, the string format and do it like so. -2:33 The next thing we actually have to do is go and download this,  -2:37 but let's just test your url really quick  -2:38 and this is not going to do much if we don't call it, do a little reformatting,  -2:43 so we are going to do that and we'll pass the code like so.  -2:46 So what zip code do you want the weather for, let's say 01010,  -2:49 see if the even works, here is the url  -2:52 we can click this, Brimfield Massachusetts, beautiful.  -2:56 So it's actually quite warm in Massachusetts for this time of year, -3:00 it looks like that's working.  -3:02 So the next thing we need to do is instead of printing this out  -3:05 we need to actually go and get this HTML off the internet,  -3:08 in Python 3 we can use something called url lib to download this  -3:12 and this is the latest version in Python 3, -3:14 in Python 2 there is actually a url lib 2 -3:17 the difference in name is kind of long story not worth going into -3:20 but this is not a super nice API,  -3:22 what we want to use is this thing called Requests,  -3:25 so we would like to use Requests to go get the data at the other end of that url,  -3:30 but Request is not something that is part of the Python standard library,  -3:33 chances are many of you don't have it, -3:35 and we need to actually go to this place called the Python package index,  -3:39 use the pip tools and download install  -3:40 and then we can use the weather app,  -3:43 that is one of the core concepts in this application  -3:45 so let's stop for a minute and have a look at it. \ No newline at end of file diff --git a/transcripts/txt/05_app/4.txt b/transcripts/txt/05_app/4.txt deleted file mode 100644 index 45670c9..0000000 --- a/transcripts/txt/05_app/4.txt +++ /dev/null @@ -1,59 +0,0 @@ -
0:00 Let's talk about the Python package index.  -0:03 You can think of the Python package index  -0:05 as the place where you can go to find all these amazing packages -0:10 that can add super powers to your application -0:13 so quite literally in a few lines of code you can install -0:16 amazing HTTP clients as we are going to with Request,  -0:19 screen scraping libraries, scientific visualization libraries,  -0:24 code coverage, testing libraries,  -0:26 you name it, chances are you can find it on PyPi.  -0:30 Sometimes you will hear PyPi referred to as PyPy,  -0:33 that is kind of considered not really correct, -0:36 there is also an alternate implementation of Python that is called PyPy  -0:39 so it's also a little confusing with that,  -0:41 so PyPi if you go back aways, people have also referred to this as the chi shop,  -0:46 building on the whole Monty Python history of Python itself.  -0:50 Now, look at how many packages are in here,  -0:52 there are 75,633 packages or libraries that you can just go out and grab,  -1:00 and bring into your application.  -1:02 Let's go to the website PyPi.python.org/pypi  -1:06 because who wouldn't want a little reputation in their url, anyway right.  -1:09 Over here, we have the Python package index  -1:12 and you can see when I took that screenshot just yesterday,  -1:16 there was 75,630 ish today, there are 75,690, -1:22 so that is around 60 new packages added just in the last day.  -1:26 That's not updating packages, that of course happens all the time,  -1:29 but new packages that were not there before.  -1:32 That is really awesome.  -1:33 Let me show you one other thing,  -1:35 it's kind of a part of the trick but it's really awesome and pretty cool  -1:38 and it makes you feel good about working with Python,  -1:40 so I told you that Python is often referred to  -1:43 as a programming language with batteries included,  -1:45 meaning it has a really rich standard library with lots of functionality built in,  -1:49 you wrap on the Python package index  -1:52 and you get 75,000 more amazing things  -1:55 and so the xkcd comic took that and did a really interesting little twist  -2:00 and so let's go over here and actually not go to the web where xkcd is  -2:04 but let's go to Python and actually launch Python 3.  -2:07 Remember the way you use any of these packages from the package index  -2:11 or the standard libraries, you say import  -2:13 so you would say import OS to work with path and so on  -2:16 but the cartoon goes something like this, -2:19 import antigravity, because in Python you can import anything it basically exists, -2:25 so what is going to happen if I hit enter,  -2:28 literally inside Python when I say import antigravity well, something pretty funny,  -2:32 it actually takes us over to the webpage where the cartoon is  -2:35 so let's check this out, so the guy asks, "How are you flying?" -2:38 "Python, I just learned it last night, everything is so simple,  -2:42 hello world is just print hello world!" -2:44 "I don't know, don't you have like typing white space" little weird,  -2:48 "Come join us, programming is fun again,  -2:50 it's a whole new world up here".  -2:51 "But how are you flying?" -2:53 "I just imported antigravity." "That's it?" -2:55 "Well, I also sampled everything in the medicine cabinet for comparison,  -2:58 but I think this is the Python".  -3:01 So this little cartoon really captures the fact that there is so many things  -3:05 you can just install and import and do amazing with.  -3:09 That is one of the reasons Python is so fun to work with. \ No newline at end of file diff --git a/transcripts/txt/05_app/5.txt b/transcripts/txt/05_app/5.txt deleted file mode 100644 index ef796a2..0000000 --- a/transcripts/txt/05_app/5.txt +++ /dev/null @@ -1,49 +0,0 @@ -
0:00 Our next concept is pip, or more generally  -0:03 how do we get packages form this Python package index  -0:06 installed into our local Python environment  -0:09 so that we can use it with our applications. -0:11 One way the wrong way but a possibility would be to go to PyPi, -0:14 find the package, the zip file, download it,  -0:18 decompress it and then run some Python commands to install it locally.  -0:22 For simple packages that would work, -0:24 although there is a lot of drawbacks,  -0:26 often these packages come with dependencies  -0:29 here we are installing Requests, but Request itself doesn't have  -0:32 any dependencies but many packages do,  -0:35 that manual download and install doesn't account for installing the dependencies  -0:40 and then the dependencies of the dependencies and on and on and on,  -0:43 the entire dependency graph, but pip handles that for us.  -0:46 Also, if you want to get an update to your package  -0:48 or even ask the question is there an update for this package  -0:52 the manual way doesn't do it, so pip is what you want to use,  -0:55 and it's super easy to use, you just say pip install and the name of a package,  -0:58 so go out to PyPi, look for that package download the latest version,  -1:02 you can lock it to a version, there is a lot of options, things like this,  -1:05 but it will download it download any dependencies  -1:09 and then execute the setup part of the package  -1:12 to install it successfully into whatever environment you want. -1:16 Now, depending on how your Python environment is set up,  -1:20 and whether it's in your user profile or it's actually machine wide  -1:24 you may have to run sudo pip install request  -1:27 because it's making a change to the global machine wide Python install,  -1:31 if you are on Windows, the equivalent of this would be to run the command prompt  -1:35 as admin when you run this command.  -1:37 It's worth pointing out that that could be a bad idea,  -1:40 when you install packages with pip,  -1:42 it downloads the package which is some random bit of code form PyPi  -1:47 then it literally runs some code from that package on your machine  -1:51 and if you use sudo, it runs it as root or admin,  -1:55 which may or may not be ok  -1:57 depending on how much you trust those packages  -1:59 so just be aware of that consideration.  -2:01 Two more quick things to note, one is if you have Python 2  -2:04 you may not have pip and if you have an old version of Python 3  -2:09 before 3.3 you also might not have Pip  -2:12 so you can use that link above to install it  -2:14 but if you have Python 3.3 or above you already have pip installed, that's excellent. -2:19 The other one is instead of using the public package repository on PyPi  -2:23 you can actually create your own hosted internal package repository  -2:28 and use pip to install and manage  -2:31 say maybe private packages for within your company,  -2:34 that's a pretty interesting way to manage libraries and packages  -2:37 across teams in large companies. \ No newline at end of file diff --git a/transcripts/txt/05_app/6.txt b/transcripts/txt/05_app/6.txt deleted file mode 100644 index 3cc9a27..0000000 --- a/transcripts/txt/05_app/6.txt +++ /dev/null @@ -1,56 +0,0 @@ -
0:00 All right, let's work with pip a little bit.  -0:02 We all use pip to install the external packages from PyPi -0:06 that we need to finish up our app.  -0:08 First thing to do is ask which version of pip do you have,  -0:11 you can type pip and something will happen,  -0:14 but depending on the order on which you've installed Python  -0:17 the way your path is setup you may end up with pip  -0:20 that manages the packages from the Python 2 interpreter,  -0:22 or you may end up with pip 3 which manages the packages for Python 3.  -0:27 So you want to be very careful on ask which one do I have.  -0:30 It turns out you can see here I have the one from 3.5,  -0:34 before I installed 3.5 on OS X I believe I just typed pip I got pip version 2, -0:39 if you want to make sure you are working with pip 3 on OS X or Linux  -0:43 you can just say pip 3, and that will always give you  -0:46 either pip 3 or they will just fail,  -0:48 similarly for Python 2 you can always say pip 2,  -0:51 sometimes it's helpful to use the witch command to say which pip 3,  -0:55 or maybe which pip and it will actually show you that works on Unix base systems,  -1:01 on Windows the command is where pip, or where pip.exe -1:05 ok we saw that pip is actually the one we want,  -1:07 I am just going to be super explicit and say pip 3 for this.  -1:10 So the first thing to do is see what we have installed.  -1:13 Maybe we already have Requests who knows.  -1:15 So I can hit pip 3 list, and then this lists all of the packages -1:19 that are installed for my Python 3 interpreter.  -1:22 And you can see Request does not appear in the list, so let's fix that,  -1:26 notice it says we are using the old version of pip,  -1:28 we'll fix that in a moment, so let's say pip 3 install Requests.  -1:32 So if I hit go, you can see I have installed this before  -1:37 so it actually grabs a cashed local version instead of redownloading it,  -1:41 have they not installed it you would have seen download first,  -1:44 the cool little progress bar and then it would have gone and done this.  -1:48 While we are at it, let's talk about upgrading,  -1:51 so pip itself is installed as a package,  -1:53 it's very meta right, pip manages pip, and my pip is out of date,  -1:58 chances are your pip is probably out of date too,  -2:00 depending on how old your version of Python  -2:03 it may be really out of date, so I can say pip install pip and do and upgrade,  -2:08 chances are this is going to fail but let's try.  -2:11 So it failed and why did it fail- well, permission error  -2:16 cannot write to this library/frameworks/whatever,  -2:21 that's a system level location,  -2:23 so for this case we need to do sudo and enter a password,  -2:29 again on Windows that would be running a command prompt as admin.  -2:36 Excellent, so now we will get rid of that warning,  -2:39 we'll be sure to be using the latest pip  -2:41 the other package that we are going to need is Beautiful Soup.  -2:45 We'll talk more about what Beautiful Soup is  -2:46 but while we are here let's go ahead and install it,  -2:48 so we'll say pip 3 install beautifulsoup now be careful,  -2:53 there is beautifulsoup and a newer which is beautifulsoup4, this is one you want. -2:58 I already happen to have it installed,  -3:01 so let's see if there is an upgrade for it. -3:04 Nope, it's already up to date, I have the latest version, that's excellent. -3:09 So we are ready to use both, Requests and Beautiful Soup 4.  - diff --git a/transcripts/txt/05_app/7.txt b/transcripts/txt/05_app/7.txt deleted file mode 100644 index 1156d5f..0000000 --- a/transcripts/txt/05_app/7.txt +++ /dev/null @@ -1,49 +0,0 @@ -
0:00 All right we are back in PyCharm.  -0:02 Now, we have installed Requests, we have installed Beautiful Soup,  -0:05 these are the packages we need to finish our app,  -0:07 but before we move on from this concept of pip and installing packages  -0:11 let's see what PyCharm has to offer  -0:13 because PyCharm actually has a really cool built in package management  -0:17 selected interpreters sort of UI here. -0:20 So I can go over here and open the preferences  -0:24 or on OS X I can hit command comma, that works in basically all apps, -0:27 so nothing special here, but here is the preferences  -0:30 and you can search for project interpreter  -0:32 or I already have it selected so it's all good,  -0:35 and notice it has the packages right here,  -0:38 these are the packages that are installed in Python 3.5 for this machine.  -0:43 And notice right here that there is a little arrow  -0:46 so it looks like there is actually an upgrade from 1.91 to 1.93  -0:51 and I can come down here and hit upgrade,  -0:53 now I know that this particular one won't upgrade successfully  -0:55 because it's actually already installed in the global Python interpreter  -1:00 and that means that I would have to do this as sudo or admin  -1:03 and PyCharm is not currently running that way so it's not going to work -1:06 but I can install new ones and upgrade the ones -1:09 that I have installed locally in my user profile.  -1:13 So let's go and actually install one, when I type pip install something  -1:17 I have to know what that something is but in PyCharm I don't, -1:19 I can hit plus and say I am interested in Requests  -1:23 and here is not just Request itself with who the author is, Kenneth Reitz,  -1:28 does amazing amazing work and has his email and the link to the home page,  -1:33 but it also has all the other ones like here is Request cloud auth  -1:37 and Requests for Facebook,  -1:39 and Requests for Facebook Python 3 and so on,  -1:41 and we are not going to do anything with those -1:44 but check out how cool this is that we can just type this in and get it.  -1:47 What I am going to install is another package by Kenneth Reitz,  -1:50 the one that he just came out with called Records  -1:53 which is a vast simplification on top of the database access  -1:58 the DBAPI2 that comes with Python,  -2:00 so I come over here and look for Records,  -2:03 and you can see SQL for humans by Kenneth Reitz,  -2:06 so let's install it, I can even specifically set a version and things like that,  -2:11 so down here it's sort of gives you a progress bar and then in a moment,  -2:15 we get package records successfully installed  -2:18 and now if we go over here and look for records,  -2:23 you can see it's in our packages and we can manage it, upgrade it and so on.  -2:26 So if you are using PyCharm and you are new,  -2:29 I kind of recommend this to get you started, it helps you discover things and so on,  -2:33 as you get more experienced in Python  -2:35 you will find yourself probably down on the command line little more often.  - diff --git a/transcripts/txt/05_app/8.txt b/transcripts/txt/05_app/8.txt deleted file mode 100644 index 2ae2c5e..0000000 --- a/transcripts/txt/05_app/8.txt +++ /dev/null @@ -1,70 +0,0 @@ -
0:00 Now that we have installed Requests and Beautiful Soup,  -0:03 it's time to use them in our application  -0:05 and this step is going to be surprisingly easy  -0:09 this is one of the reasons that working with Python is just so super awesome.  -0:12 Ok, so you can see on line 23 I've written Requests -0:16 and this is how you get started with Requests, -0:18 but there is an error and there is a red underline, -0:21 but it's the kind the PyCharm can fix,  -0:23 and what we need to do of course is import this external module  -0:26 so at the top I would type import space requests  -0:28 and I will come back down here and I will start typing you know,  -0:31 calling methods on it and so on.  -0:33 But, in PyCharm you just hit alt enter,  -0:35 and you say I would like to import this please  -0:37 and it sort of adjust the scroll so that you don't get a jerky feel  -0:42 but there is at the top import Request.  -0:44 Right, so I encourage you to let PyCharm help you.  -0:47 Now we can say get and you can see it takes a URL, some parameters,  -0:50 some kwargs, that's keyword arguments,  -0:54 all we really need to do for this simple case is just put the url here  -0:57 and we'll say the response that comes back is whatever goes there.  -1:02 Let's start by making sure that we get  -1:04 a good status code from the web server -1:07 we want a response code of 200,  -1:09 response code of 404 or 500 not so super awesome,  -1:12 so let's just print out the response.status_code. -1:17 And so we get, so again it's asking what is my zip code  -1:20 so I'll say it's 01010, which is as we saw a Massachusetts  -1:24 go, wait for a second for the web server to respond and 200, perfect. -1:28 So that means we are talking to this web server,  -1:31 you saw there is a little delay,  -1:33 that is just the web server actually trying to generate the full page, and that's great,  -1:37 but what we really want is the HTML.  -1:41 So we can come down here and we can just print out response.text,  -1:45 now we want the actual HTML so we are going to do response.text  -1:49 if it was a json response we could do like json  -1:52 and convert it from json into a Python dictionary things like that,  -1:56 but what we want is just text,  -1:58 and now this is pretty big page so I want just a small portion of the text,  -2:01 in Python there is a really cool thing  -2:03 you can do to lists and other collections called Slicing,  -2:07 so I can say I would like just from the 0th item to let's say the 250th item right,  -2:13 so that will give me the first 250 characters  -2:15 and whatever came back from web server, let's see what that is.  -2:20 And ta-da, we get just the top little bit of the HTML page  -2:25 and you can see already perfect, we are getting Brimfield Massachusetts 01010,  -2:29 OH! this slicing concept is really powerful,  -2:32 and there is a lot of ways to use it,  -2:34 I could say I want the 0th to 250th item like this,  -2:37 or when you start at the beginning you can omit this,  -2:39 you can also say I would like from the 250th item onto the end like this  -2:45 or the length if I knew how long it was,  -2:47 but we are just going to go back to this basic version here.  -2:50 Now, this is not actually the purpose of this method,  -2:53 they just print out a few characters, -2:55 remember the name of the method is get HTML from the web  -2:57 so we saw that the HTML is already downloaded  -3:00 in the response object and it's just the text.  -3:03 Beautiful so where we are calling this function  -3:06 we should be capturing the HTML  -3:08 so you see we are just throwing it away right now,  -3:11 we'll say this HTML equals, and we'll have gotten HTML from the web,  -3:14 now I don't feel like we need this comment anymore,  -3:16 get HTML from the web, to call the function get HTML from the web  -3:19 that's the beauty of functions and choosing good names,  -3:22 so the next thing we need to d is parse the HTML there is get part,  -3:25 this was Requests, there is parse part, that is Beautiful Soup.  -3:30 Before we get to Beautiful Soup though,  -3:33 this concept of slicing is super important  -3:35 and let's take a moment to just explore that further.  \ No newline at end of file diff --git a/transcripts/txt/05_app/9.txt b/transcripts/txt/05_app/9.txt deleted file mode 100644 index cc8eb34..0000000 --- a/transcripts/txt/05_app/9.txt +++ /dev/null @@ -1,60 +0,0 @@ -
0:00 Let's talk about slicing.  -0:02 You can slice all sorts of things, you can slice strings, -0:05 you can slice lists, you can even slice results that come back from databases -0:10 you can have that slicing due some kind of paging on the database side on the server, -0:16 it's super powerful and it's one of these concepts  -0:19 I would classify as Pythonic meaning that this is a programming idiom  -0:23 or style of programming that is somewhat unique to Python.  -0:27 So here I have a list of numbers which I've called nums  -0:30 and it happens to be the first 9 prime numbers,  -0:33 I can do things that are really obvious like get the first prime  -0:36 by saying nums [0],  -0:39 remember, accessing elements in lists and all sorts of things in Python is zero based -0:44 so it starts counting at 0, 1, 2, 3, so the first one would be nums of 0,  -0:47 and you can see on the comment on the right that would return 2,  -0:51 something we haven't talked about which is also fairly unique to Python  -0:55 is that you can use the negative indexes,  -0:57 so instead of having to somehow do some math, -1:00 taking the length of the numbers into account  -1:02 and subtracting 1 from them, to get the last item, we can actually use -1:06 here we can use minus 1 and that will give us the last number  -1:10 so if we put minus 1 in there we get 23,  -1:13 but those are just regular array indexes,  -1:15 however if we give it a range separated by colon then we get slices,  -1:21 so if we want the lowest four prime numbers  -1:24 we would say as you saw in our HTML example 0:4  -1:27 and that gives us the first 4 items,  -1:29 but there is the shortcut here where we can omit  -1:32 the beginning if we are starting at 0, -1:34 or the end, if we are going all the way to the end. -1:36 So these two statements are equivalent  -1:39 we can also get the middle ones so if we want the 4th or the 3 index 0 based item  -1:45 and then 3 more, we can say 3:6 and that will give us 7 through 13, -1:51 and we can also go to the end just like our sliced one  -1:54 from the beginning so we could if we computed the actual link  -1:57 that for some reason we knew it because a 5:9  -2:00 now in programs this is of course very dynamic  -2:03 you rarely have a fixed link you just hard code in there,  -2:07 so this would be something like the length of numbers  -2:10 minus 4: the length of numbers this is not cool,  -2:13 but it does give us the result we are looking for.  -2:15 What is more cool is to just say I would like to go from the 5th item onward,  -2:20 that still requires you to know the length of numbers -2:23 if you want just the last 4, so we can use this idea  -2:26 of a negative index kind of like I talked about  -2:29 for indexing here but for ranges,  -2:31 and we can say I don't know how long it is  -2:33 but I want you to go to the end and then go back for  -2:36 and then go all the way to the end,  -2:38 so this -4: nothing will actually give us the last 4 items without  -2:43 us caring what the length is.  -2:45 It's worth noting that when you are doing these slices  -2:47 this type of stuff here you don't get errors  -2:52 if you use numbers that are too big or too small.  -2:54 So if I said 5:1000 and there is only 10 items,  -2:57 it would just give me the last 5  -2:59 but up here where I am using array indexes  -3:01 if I say give me the 1000th item and there is only 10- boom, that's a crash. -3:06 So these aren't quite treated the same way in that regard,  -3:09 but otherwise you can think of using these ranges as indexes  -3:13 into the list and what you get back are little smaller lists. \ No newline at end of file diff --git a/transcripts/txt/09_app/1.txt b/transcripts/txt/09_app/1.txt index bd71f36..7e32417 100644 --- a/transcripts/txt/09_app/1.txt +++ b/transcripts/txt/09_app/1.txt @@ -43,6 +43,6 @@ 2:17 of writing code that has to run on both,  2:20 on Python 2 and Python 3, as you saw,  2:23 we are talking about averages and there is a nice statistics module  -2:25 that was introduced in Python 3.4, it is not available in Pyhton 2,  +2:25 that was introduced in Python 3.4, it is not available in Python 2,  2:29 so what will we do there? Well, you'll see that we can write the same basic code  2:32 and just adjust our imports and make it work. \ No newline at end of file diff --git a/venv_on_windows.md b/venv_on_windows.md index dcf2f56..1930acc 100644 --- a/venv_on_windows.md +++ b/venv_on_windows.md @@ -3,24 +3,93 @@ Unfortunately, the windows installer doesn't provide full parity with the richer setup on unix-based systems (namely macOS and Linux). Here are a few tips to help. +## Bash or Powershell? +>>SideBar: Bash needs no preliminary setup, but before proceeding in Powershell, a one-time update of ''script execution rights'' needs to happen in Powershell. << -## Checking active versions +The one-time Powershell update requires admin rights, so open PowerShell(Admin) and do this: +- key in `get-executionpolicy`, hit enter. If the result is `RemoteSigned`, your execution rights are good and you are ready to create a python virtual environment -In the course, you see me using the `which` command. Windows has an equivalent command called `where`. +otherwise, do the following 2 things -This has nothing to do with Python's setup of course, but often you need that command so we start here. +- key in `set-executionpolicy allsigned`, hit enter. Type **Y** , hit enter. +- key in `set-executionpolicy remotesigned`, hit enter. Type **Y** , hit enter. -## Activating +Now you are ready to create a python virtual environment. -Once you've created a virtual environment via -`python -m venv FOLDER_NAME` +## Check which python version is active in your shell -(Make sure this is python 3: `python -V` -> 3...) +In the course, you see me using the `which` command on my Mac. Windows has an equivalent command called `where`, as follows: -You activate it differently. It's `activate.bat` is in scripts not bin (why?): +- in Powershell for Windows -`FOLDER_NAME/scripts/activate.bat` + `where.exe virtualenv` +- in Bash for Windows, both commands work + + `where virtualenv` or `which virtualenv` + +This has nothing to do with Python's setup of course, but often you need the 'where/which' command, so we start here. + +## Create the Virtual Environment + +To create the virtual environment ( aka `venv`), type this command and hit enter: + +`python -m venv FOLDER_NAME` + +`venv` uses the version of Python installed in your system PATH. **FOLDER_NAME** is being created, and can be a subfolder in the directory you are currently in OR a fully-qualified path to another directory, as shown here: + + python -m venv py36_weatherapp + or + python -m venv 'C:\Users\Rihanna\py36_weatherapp' + + +## Activate Your Virtual Environment + +In Bash, change directory to FOLDER_NAME/Scripts and key in `. activate`, hit Enter. (that is dot-space-activate) + + BASH Example + + ~py36_weatherapp (master) + $ cd Scripts + ~py36_weatherapp/Scripts (master) + $ . activate + + after the activate you should see FOLDER_NAME name in parenthesis: + (py36_weatherapp) + ~/py36_weatherapp/Scripts (master) + + + +In Powershell, change directory to FOLDER_NAME and key in `.\Scripts\activate`, hit Enter. (that is dot-backslash-Scripts-backslash-activate) + +Or drill down to FOLDER_NAME\Scripts and key in `.\activate`, hit Enter. (that is dot-backslash-activate) + + Powershell Example + + C:\> cd Users\Rihanna\py36_weatherapp + + C:\Users\Rihanna\py36_weatherapp> .\Scripts\activate + + OR + + C:\> cd Users\Rihanna\py36_weatherapp\Scripts + + C:\Users\Rihanna\py36_weatherapp\Scripts> .\activate + + + after the activate you should see FOLDER_NAME name in parenthesis: + (py36_weatherapp) C:\Users\Rihanna\py36_weatherapp> + + + +After activating, at the prompt, check which version of python is being referenced by the virtual environment by typing in: + +`python -V` or `python --version` + +Now your virtual environment is ready for your code. +##### In Pycharm: File-->Settings-->Preferences, to navigate to the virtualenv menu + +Type `deactivate` to exit the virtual environment folder, regardless of how you got there. ## Enabling `python3` and `pip3` commands @@ -38,5 +107,39 @@ That will run the local python and pip or the one first in your path depending w This may make following along exactly with my commands easier. +## BONUS: Virtual environments in Windows for `python` older than `V3.3` +Instead of using `venv`, the command for creating a Python virtual environment for older Python versions is `mkvirtualenv`, as follows: + +`mkvirtualenv` `--python=c:\Python27\python.exe` `'C:\Users\Rihanna\py27_environ'` + +where you point to the location of the older executable (the ".exe") file. + +`activate` and `deactivate` work the same way. + + +#### An Example Showing Python Version if Virtual Environment is Active or Deactive + + PS C:\Users\Rihanna> cd py27_environ + + PS C:\Users\Rihanna\py27_environ> python -V + + Python 3.6.5 + + PS C:\Users\Rihanna\py27_environ> .\Scripts\activate + + (py27_environ) PS C:\Users\Rihanna\py27_environ> python -V + + Python 2.7.1 + + (py27_environ) PS C:\Users\Rihanna\py27_environ> deactivate + + PS C:\Users\Rihanna\py27_environ> python -V + + Python 3.6.5 + + PS C:\Users\Rihanna\py27_environ> + + +