Determining Air Quality around the world via CLI | Rustlang Let's Code!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome back to another let's code today we're going to be working on a cli application in rust that will get us the weather and air quality for any given place that we are so the air quality api that we're going to use is aqicn.org api if you don't have a token and you want to follow along you need to click on this link and then fill out this form they will send you an email at at which point you will get a token so for this project i'm just going to start a new cargo project called aqi aqi is going to be our binary name let's go into aqi and i open up a vs code instance vs code wants to update yay that's exactly what we want no we're going to hit later so we have a cargo application we have a cargo tunnel we have a main.rs the main.rs is our binary the cargo tunnel is any dependencies that we'll have or anything like that i know of a bunch that i want right off the bat so we've got color air which will handle all of our error handling for our cli app structure does going to do argument parsing and things like that request is going to be the thing that we use to call out to the api to get the request back or get the data back saturday and saturday json are going to be the deserialization or serialization stuff that we use to parse the json from the api into rust stuff and then tokyo is going to be the async runtime that we use to allow request to be an async library which it doesn't have to be because requests can be blocking as well which actually i covered in a simpler version of this weather cli so if you're interested in that yeah restaventure.dev sign up for the email course at the bottom and you will get a run run-through of the simpler uh rust weather cli uses the same api um just explains sort of like it as an intro to rust and rust features let's see um if i remember correctly code uh config path will give me my new shell config perfect i need to set my api key in here uh because i'm using new shell if you're not using new shell you'll be fine you don't need to set it i just don't have a way to revoke this key so i'm not going to show it to you my name is aqi token cool so i have it set in my environment variable now i just need to remember that i called it uh aqi token when we use it later so let's get into it um so the first thing we're going to do is tokyo main which rust analyzer has identified a problem with the problem actually is that we didn't ask for the ability to do that in here so cargo has a feature called features which i've talked about before and we can just do features equals i think it's macros for tokyo yeah it looks like macros so we should be able to cargo build um and this will download all of our dependencies and stuff so that's going to take a second in the meantime we'll keep going and if i've made a mistake then uh rust will tell me in a little bit so we are going to want uh some stuff from color air and one of those things is air and i think result is top level so we're going to make this into a result of unit so that we can do some error reporting and handling from our cli i'm using color air and not something else because it is um oh okay so async function here needs to have um or tokyo main needs an async function to turn into our main function so that's one of the reasons our build just failed did i mess something up the default runtime flavor is multi-thread but the rt multi-thread feature is disabled uh okay so i need to re-enable one of the let's just we're gonna go with all for um tokyo right now does it not have all tokyo doesn't have all tokyo rs is the runtime stuff uh i don't really want the api docs full full is the thing that i wanted full is the um thing that will just let me like do stuff and then we can take it back down later if we want to uh one of the things that we need is macros which will let us use this macro we also need runtimes and stuff like that but full is fine for us for now in the end all it will mean is that we have a slightly bigger cli in the end than we wanted but it doesn't really matter that much expected result found unit so we need an okay unit here because we do need to return this unit type for the result result is either okay unit or it's an error with an error air will help us construct those errors later so yeah so if we cargo run now we get hello world we're not doing any async stuff yet we just have the runtime running um we're not doing any error handling yet or anything like that so if we do like this instead um it's not returning this because rest is an expression-based language and i had something else there so i would need to wrap this in air e-r-r e-r-r and air e-y-r-e sound way too similar um is that not the macro is it air air i thought the air i thought air the macro was the top level but apparently it's not air air so you can see over here on the left that we've got hello world and location whatever um we haven't initialized air yet so we're going to do color air install that's actually going to have the potential to fail so we'll handle it with the question mark which will pop it back up to the result and now we get actual colors here and a little bit more reporting you'll see that before we actually installed color air we were getting kind of like this bland error that just had like a location the file says or the location in the uh file and stuff like that and now it has this nice red like hello world uh and where it went wrong and we can add more context to this which is really nice so we'll do some of that later let's see what do we need to do here we need to set up our cli next i think maybe let's make a request first let's get rid of this let's bring this back let's set up a new request client so let um client equals request client new because we don't need anything special yeah and we just get a client back from that and we're just going to use a get here so get and then i don't remember the url so i need to go find the url i have found the url to make this a little bit bigger uh this will pop on to the next line after we keep going here i believe we just got send here if i remember correctly um and this is going to be async so we'll await it with the question mark and if anything goes wrong that error will pop back up uh and we'll get it here just like we get this error if not we won't list the bug response to make sure that we're getting something here i don't remember if we need to deserialize this or not apparently we don't uh scheme https response url headers aqi path so we do need to do a so let's check out the error that we get here actually so we have a response struct on the left right so it's not actually just like random stuff this is actually a type from the request crate so it's a struct that has say like a url field and a url struck inside of it and this is like the request as we uh the response i guess as we got it back so if we try to run this uh the response.json method is not found in response that doesn't feel right let me check the request docs we'll go request rest we'll go to the docs.rs link you can see up here docs.rs so this has dot text did i put it in the wrong spot i think i put it in the wrong spot so get send await then i would expect dot json to be here and they're awaiting on the text so i'll do that here too but it says that dot json doesn't exist and i think it's gonna do that again so what did i do wrong did i not import something the post json posting json batching json let's look at client because i did something wrong so that gives us a request builder the request builder dot send is what we're using right great feature json only uh that might be important too concerned about it yet so send gives us a response or response is the type that we just saw which means that if we scroll down here we see text right here text with car set yeah okay so they moved json to a crate feature and you can see that that was uh specified in the docs but i did not enable it so we'll do that and we'll do features equals json json this can be why even if you think you know the apis it's useful to read the docs so let me get rid of a couple of these um because they don't i don't need all of that um this is a future so we'll dot well let's let's keep the json here i suppose so dot json because that's what we're expecting right and then this is the future again um and we know it's the future because it says impul future right here um and it says that when we're debugging um or when we're using the debug macro to print out the debug formatted json response uh that that type is an impul future and impul futures are things that we can await same thing right here where we're awaiting this actual get request um because it needs to go do some work and then give us control back basically so that's how i knew this was a future that's how i knew i needed to await it so if we await it we should get a different error which will be um oh wait is that not of course it's not a method yes it's a keyword so a weight is a post fixed keyword not a method goal that makes total sense error decoding response body invalid type map expected unit of line one invalid type map expected unit at line one so what i was expecting is to have to do this let me just see oh okay yeah 404 on beijing that doesn't sound right okay so what happened here was because i didn't provide a type 2 json which it kind of needs it tried to infer the type and when it inferred the type i think it got it from debug and the type if inferred that way is unit so it says expected unit expected unit unit is the type the type of unit is this double paren up here um it's the type we're returning down here as a value so it was like we wrote this instead which is not what we want we want saturday just on value here for the moment anyway so we got a 404 for beijing um i believe that is because we aren't using the api token at the moment so we do need to fetch that um we can use standard m and then up here i will let token equal m arg aqi token because i think that's what i have set this can fail so we'll add a question mark on the end uh and var not embark and varg would be for cls stuff and it would be args because of how that's handled um so we actually do need the query string here uh already i believe uh because i think if i remember the documentation that's how this was set up so we will um pass in an array here of i think it's an array let me go check the request docs or query uh this is in the response so let's go back to request builder and let's look up query query is somewhere in here so modify the query string yadda yadda yadda this message does not support serializing a single key value pair instead of using dot query key val use a sequence so we can't use a tuple here we have to use uh like an array or a back or something like that which is totally fine it's also possible to serialize structs and maps into a key value pair we don't really need anything complicated um all we are going to do here is take a reference to an array that's called token and takes a token and it's a tuple on the inside so we have to wrap this whole thing in a tuple and then that should give us a valid response so if you don't have your oh well that also did not work let me do bug out token make sure we're getting it i'm just going to cut this part out of the video so i debugged out token here you can see that i took a reference because we're also using it down here and if we don't take a shared reference to token when we pass it to the bug uh debug the token will move into the debug call which we don't want but it is there um so something else is wrong and beijing is the example in the docs so i think it's something i'm doing wrong if you tell me if this that hurts a lot that hurts a lot um so if you are following along and you got the error that we just got before which is a 404 on this request make sure that you have the slash at the end the slash at the end really really matters so we were actually passing in our token everything was fine we just didn't have this slash at the end so yay programming can sometimes be frustrating okay so we can get a request for a specific place there is a second url that we can use for searching and that's api.waki.info search instead of slash feed slash station so i think when we do our cli here we'll be able to get either a specific place and uh more info or you'll be able to search for more stuff um so let's get that in here i think so struct up well yeah opt is usually what this is called we're gonna derive uh struct opt which i believe we're going to have to actually import use struct opt struct okay so now we have struct opt here um we will have to do or [Music] which things are here i think it's from args what that we want um i think this can fail maybe it can't can't fail so we're good so we have args args is going to be all of these things that we passed in i'm going to move this low aqi token because we're actually going to get rid of this a2 aqi token call in a second um because we need the token for everything right so in here we're gonna have opt and name this like api token or something like that make it a string uh we need to use a field level attribute macro here so it'll be called struct opt and you we could make this passable via the cli like as a flag um i think that we will do that actually so short equals t uh because otherwise it would be t long equals api token right or maybe it should just be token let's make a token and then end equals api token aqi aqi token so now we've got uh an opt struct right that has this attribute macro on it that looks kind of uh i don't know i guess it's intimidating right um and we have one field which is an api token that's a string and we've derived this struct opt derived macro on this opt struct um if you wondered where the name struct opt came from it's from struck opt um so we can get rid of this nvar now because args.api token will be uh we'll fetch it from the environment variable if it exists for us so args.api token and this should all work as it did before because the environment variable is still set it did wonderful so now we've got this way to set up sort of like additional arguments that we need for our cli um and the next thing that i think we're going to do is set up a sub command so i'm going to name this command and it'll be actually i'm going to name this opt i think i'm going to name this aqi and this will be aqi and this will be opt and we'll have a second struct here called struct opt um it won't be a struct actually it'll be an enum enum opt uh up here like struct opt is very heavy on the attribute macros um so we do need to use those again in this case what we're doing is basically telling structop to treat this field as a set of sub commands for our whole thing here um why is this complaining struct opt needs to have the derived macro on it as well so what we end up with here is basically these are global options or global flags or global whatevers in this case is an api token right so we get a api token specifically we have it set in the environment you can also set it inline as um the dash t flag so like we could do aqi t and then like r token or something like that or dash dash token and then our token or whatever but really what we want here is aqi info beijing and aqi search uh like whatever like um san francisco because that's where i am right now so in this opt here we're gonna have an info and a search and we will mostly set this stuff up later so we're going to come back to this and we're going to add some more fields onto info and search this is very much like an option type or a result type or something like that basically we have this opt enum that can be one of two values it can either be an info value or a search value and one of the really nice things about rust enums is that we can attach data to them uh so basically these can be like substructs if you want to think of them that way but it either is an info or a search so they're mutually exclusive which means that we can use it as the basis for our sub commands so if we have the args here right and we've instantiated a new client let's say match args dot command right so this is going to be the sub command i could have called that sub command i didn't and then we're just going to go like this right uh or that's going to blow up in our faces why did that blow up in our faces oh right so i don't have a semicolon here um that is one of the reasons it blew up in our faces there are multiple let's see what's up i believe that i messed up oh i definitely messed up something what do we have here syntax wise uh let response equals let response equals that response equals oh i'm not matching on anything let me pull this back out for a second so what we need in here uh and rust analyzer can write this for us is actually opt info and opt search so we need to match on the two sub commands to know what to do and if it's opt info then we're going to make this request and if it's opt search then we're going to make a slightly different request and this is going to be search and the search token um let's see do we have the api docs here json api search search stations by name where's the search api our online api documentation map queries search there we go so we need to name it keyword so we have our token our token is already in we need a keyword we don't need to deal with jsonp we're not using javascript so this needs to be a second tuple where the first element is keyword and we'll do i'm going to put san francisco here because we don't have the option coming in yet this does need to be a full string maybe yeah it does so string from san francisco and then we've got basically when we search we hit this search endpoint with our token and the keyword in this case i've hard coded it but we'll get that from the cli in a second uh we're getting back a saturday json value because we don't really know what the return values are yet and if we're uh being honest the return values for this api are not actually well documented so i know there are there are things here that look like uh documentation but if i remember correctly from using this in the past not all the fields exist and you can see that there are no actual like there's no marker about whether these are required or optional so some of the keys might not exist but this is roughly the data that we can expect back so if we cargo run we are going to need to use dash dash so that any options that we pass in actually go to the command line application rather than cargo itself so if we search then what we get back here is hopefully a list of yeah country u.s san francisco uh of albuquerque new mexico not the one i was looking for but it is the san francisco so san francisco new mexico i assume that san francisco california is in here somewhere that would make sense um but you can see that we have some of these uh some of these ids these actually can go straight into the urls so if we wanted to actually search and then select one and then use that as the next request right here uh as the path segment where beijing is we could do that and i think we actually will there is a library that we can use for that that is a rust crate called skim i've never used skim before so we're gonna we're gonna take a look at that together but let's get this a little bit uh let's print out some of this let's say what do we have here let's start with info so info here well i'm trying to think about what's the easiest way to do this is because if we search and we get this it doesn't look great let's do this let's do two string or saturday json there's a certain json two string function that takes a string reference uh or takes a a reference to the value and when we run this we'll actually get a json dump here instead of uh oh i'm debugging it i need to print line it i need to print line it because otherwise we get all this escaping and i don't want the escaping doesn't implement string um why is this oh okay saturday json2string actually can fail so uh that's why the display formatter is not implemented for it because the display formatter as opposed to the debug formatter is not implemented for result or error i forget which one doesn't really matter for us but now we can actually see there's a bunch of json here and if i want to be super neato i can just pipe this around because i'm using new shell so i'll get data and we can just play around with this a little bit um let's do first five four four because i hit four instead of five and then we can see that we have the aqi or the air quality and the station inside of the data um let's pivot this so we can see a couple of these so we've got aqi station time and uid so let's we're probably going to drop time and uid we'll keep aqi and then we'll have station let's see yeah station has to offer the station has the country the geo the name and the url so i think we can get something reasonable out of this for now um and then come back later and do kind of a prettier output for it what did i say we had here we have data and then station is the interesting one aqi so aqi and station so let's start building a struct here what i'm going to do is struct search response something like that where search response has data which is going to be a station aqi station aqi does that make sense let's destruct station aqi uh has an aqi field that is a i don't know like a u32 i think this only goes up to 500 so we could do like a u16 but i also have a feeling that dealing with different numbers will be easier and we're not really worried about space here so station here will be a station and then a struct station will be actually you know what let's let's cut it off there um and let's say that this is a saturday json value instead so we need to now derive the serialize which i don't think we've imported yet or brought into scope not import so at the top here we have uh certain and then it'll be deserialized and we are not using standard m anymore did i make a mistake and using that search oh you know why we can't find the macro i always forget to turn features on uh i swear it's the one thing that like is the my huge downfall when working with rust i basically like always forget i think this is derived so if we use derived there then we can derive these stereo lives for our two structs here and then if we take search response i believe search response can be our new return value as long as this doesn't break okay cool so let's make that a search response um i don't think we have we haven't implemented serialize so this code that we have here won't work anymore because we're serializing the whole thing here right um so let's do this for uh station in response dot data right did i do that correctly i didn't do that correctly i don't think i defined data correctly this is going to be a vac of station aqis so for station and station response.data then we're gonna print line this and do station dot aqi and then we'll just get rid of this for the moment we already kind of know what we're dealing with here so if we get a bunch of aqis then we did something good if we didn't like this then we did something bad you know what the problem is here the problem is that the aqi is not actually an integer when it comes back from this uh api and i'm not sure why it's quite like why it's not an integer because i i think this number doesn't really get that high it only goes from like 0 to 500 or something like that oh it's a string because we can have dashes so this is another thing about this data set it's kind of messy so when they say they are going to give us a string it's actually because they are doing things like this and giving us like dashes instead of numbers which is fine this at least gives us a list of um stuff so stations i think stations have names but uh for the moment i think that i am going to be uh tired of writing out more structs so i'm gonna do station dot ooh what is it station dot name dot as string dot unwrap so hopefully oh i didn't access this correctly so um there we go we need to access it like that because it is a saturday json value and we are getting the name field off of that and we're getting the name field as a string because otherwise it's just another started json value because saturday json value the thing that we're using here includes strings arrays objects everything that's valid as json top level keys so we have to specify what type we want it as which is part of the reason that we're defining these structs in the first place because getting a saturday json value here is really for when we really don't know what we're going to get and we kind of do know what we're going to get here at least for the stuff that we care about right we do know that there's going to be a data if it's successful we do know that there's going to be an aqi for basically all of these even if we don't have one and we know that the station is always going to be here with a name and a url so i will probably i'll probably actually keep that going let's do struct station name string um url string i can't think of anything else that was in there that we cared about a mole derived oh if i can type we'll derive uh deserialize so we're just making it so that we have more stuff available basically and then this will be station we'll have the name and the url available and it will be obvious so we won't need to do this as string anymore and we can do dot name here and we don't need to unwrap because if it's not there then it's invalid which saturday now takes care of so let's do how are we going to do this how do we want to do this let's do um this here and we'll do station.station.url as like another line underneath we will do hmm do these all come in as two digit numbers are we just lucky like that i do kind of want this to be table-ish but we're not working on it being pretty yet we're just working on it so let's do a little bit of padding in the formatting string so rust format padding i forget what exactly uh the thing is here it's like yeah okay so this is zero padding i think that this might be the character that we want to pad with there's a bunch of different formatting like stuff we can use so yeah we can pad this to two spaces by using uh colon two which gives us a little bit more of a table output i suppose the other thing that i do is if i was going to keep consuming this i could put a tab in there and that would work fairly well for new shell but i think that's good for now for us let's do a new line here as well that new line is intentional let's just give it a little bit of breathing room so now we have the aqi for a particular station uh that we've searched for we've searched for san francisco arkansas street wherever that is um san francisco california this is the url that we would have to use so we can sort of like if we know what we're doing we can look at the url and be like oh yeah i wanted the one in mexico um or something like that the one the the one at the airport the toluca so that's good for search for now i think we'll use skim here later to actually let you select one to like search for a station somewhere and then select it and then use it so we need to we need to get beijing in here to be an actual user type so i'm just going to format this string and i'm going to use the display formatter and right in here i'm going to what is what are we going to call this args um let me just do this as uh beijing for the moment so we can format this string using the format macro format macro lets us place these braces these curly braces wherever we want this variable to go into and that should work if we do info we don't have the nice output like we have for search so we will need to actually take care of that let's do the same thing we did before and do the kind of like pretty printed output right so let's make this that let's do saturday json uh tostring the response we don't have an actual type here yet so it's all good we'll print line it and that will give us uh oh it won't give us because i messed something up ah right i did the same thing we did before where i didn't use the question mark or go run info and now we have all that so let's do uh from json and i will say data or status so we have data again uh get data will give us the data let's pivot this to see a couple of the actual column names so we've got aqi for beijing the attributions the city something called the bug summon it called dominant pole which i think is dominant pollution polluter so pm25 is a chemical um the forecast which could be interesting for us um the iaqi which i don't actually know the index and the time i don't know what iaqi is i wonder if we can find that in the docs so iaqi is the measurement time information this is the city station feed this is the right so it looks like that'll give us uh keys and values so this would be a hash map okay so we've got uh our response here we've got a data field we've got the aqi which is going to be uh important we've got the forecast which we can use at some point so let's pull in the forecast let's say we've got not a search response here now but a and we'll blend arrive to not a search response but an info response where the data is a vec it's not a vec actually um the data is a station info and we're going to get derive in here again serialize struct station info which will be aqi which i assume is a string again let's get the attributions in here because that seems like a good thing to have like where is this information coming from right or like who should we thank for this both of those sides of it um i guess like if there's more attributions maybe it's more trustworthy i don't know where those attributions come from uh city do we want city what's in city we want forecast uh i think that's maybe iaqi which i will make uh a new thing um maybe i won't make this a new thing you know i think that saturday json value gives us what we want for iaqi because one thing that i do know about this data set is that the um iaqi and these chemicals as we can see even right here we have 10 results for this in our current query but we only have one here so we have pm25 with a value of 71 and the forecast may or may not be there it depends on whether the station keeps that data or not i believe um so we may get like pm 25 uh longer term data but we may not it may be missing days for example um let's see we get city name url geo so maybe we pull the name out of city we've got the aqi the air quality is the main thing the attributions uh it doesn't show the attributions here object epa attribution for the station okay so it doesn't actually tell us what attributions are so we're going to leave that as saturday json value because we have to the forecast will come in maybe we'll use that later maybe we won't the iaqa the iaqi um i don't know what the i stands for individual aqi ah i got you so it's uh the aqi i assume is a combination of all the other chemicals that are around the iaqi is all of the individual values so let's pull this in let's start using it as the value here for our request so you can see that json is a very generic function it can deserialize into whatever type we want it to um let's do for actually there's only going to be one right so response dot data dot what aqi let's do let data equals response.data for kicks we'll do print line um and then we'll have something here and then the next line we'll have some stuff we'll have aqi here right so let's do response dot data dot what do we have what did we put in here i already forgot um response.data dot oh i don't have the city in here we should have the city in here let's do city i'm gonna make it a a capital c city um i'm gonna put a derived here they serialize uh the struct city will have name which is a string and we don't care about the other fields right now uh did i mess up a comma somewhere why is it yelling at me all of a sudden i missed like a parenthesis or something here did i not finish this i did not finish this there we go dot city dot name and then data dot aqi right let's get the response out of here data.city.name data.aqi let's get rid of this and let's run it cargo run dash dash info to get the sub command error decoding response body integer 59 okay so expected a string the only thing that we are expecting a string for that could be a number is the aqi so the aqi and one of our requests is a string and then the other one is a number so be aware of that if you're using different urls from this data set um the types for the same name change based on where you're looking for it it's a pretty great example though for a real world uh use case so we've got beijing of an aqi of 59 i think that we can keep this for the moment right so we do args up here for our cli arcs for info we need like we need a url is what we need right we need the url and we need to be a string so i guess we make that positional and a positional argument as you'll see in a second is uh the ability for us to just let's see data.akui and then we don't need more there we need this up here so we need um args is it there's args dot i need rust analyzer oh you know what the problem is i need to destructure it here the url so i'm gonna go url here and it's really not url i guess it's a slug right so like this slash needs to be there which is really unfortunate so let's do url dot trim start matches slash dot trim and matches slash and that should give us um like if somebody does slash san francisco this will protect us uh and we can then just put that in right here one other thing we could do if we wanted to is turn this url into a slug there's a library called inflector on crates.io that would let us do like um like two kebab case um so we could basically sluggify that uh actually let's do that let's let's do two kebab case because i kind of want to be able to write that reflector and we need to bring in the trait because it's an extension trait um inflector elector uh did i mess up i think that rust analyzer just didn't realize that it's here yet but struck top should uh give us an error because we didn't pass in yeah so now we get this really nice error here that's like the following required arguments were not provided url and then because we use struct opt so one of the really nice things about struct opt and using uh like clap or struct opt or something like that is that we get aqi token info url so url is a positional argument here so we can do like san francisco and uh because we're doing inflector it translates that into san dash francisco all lower case which means that we get the san francisco in san francisco california which is the one that i was looking for because that's where i am so we could do other things like um like if we only have one word we can do italy or what not um that'll just give us like kind of a random place in italy right um if we search for italy i wonder which one it gave us oh right we didn't implement that yet let's do that let's implement search this will be search for i don't know we'll call it let's not call it query let's call it search or terms which is a string and then down here we will destructure that so opt search on this enum we get to call terms and terms will be here and we just get to pass it in so like before we're passing something in for keyword here but in this case it's just we're passing in whatever the user gave us so if i search for italy it should give me a readout and i probably got i didn't get that one which one did i get one of these is ha one of these is probably just called italy i wonder if it just gives me like a match i think it was one of the so this isn't something that we did this is something that the api is doing so we could stick it in here if we wanted to right like url city oh that's not what i wanted to do url data.city.url and then up here all we have to really do is say there is a url here and because there is one we can run this with italy again and see which url we actually were getting and it is actually the one huh i don't think that one exists when we search italy how's it nope it's this one yeah okay so is it that one no it's not that one i can't even find it i don't know where it is i don't know why the api is giving us a different result here um but maybe it's maybe it's just kind of like picking one but now we have some output we have output for um info we have output for search okay so there's plenty of stuff to do here right like um let's restart rust analyzer because i don't like looking at red squiggly lines so we have search right we can search for italy let's search for um i don't know london something like that gives us a bunch of london results let's do um info and let's pick this one specifically which hilariously is not actually anywhere near london it's in new hampshire um unknown station so this url actually doesn't work for that if we just look for london then we get uh the city of london and the aqi in london is 85. so that's that we have search for london and we also have london itself so there is plenty more that we could actually do here um the episode is getting a bit long so i think i'm going to cut it there but i am going to turn this into a course and we are going to do things like uh add a little bit more color to this like it would be really nice if this color here showed as green if everything was good and red if everything was really bad etc and thanks for hanging out for another let's code um maybe i'll work on this again next week maybe i won't if you have an opinion let me know on twitter let me know in the comments and i will see you in the next video
Info
Channel: chris biscardi
Views: 607
Rating: undefined out of 5
Keywords:
Id: Cm6pzNJx2XY
Channel Id: undefined
Length: 41min 9sec (2469 seconds)
Published: Mon Sep 20 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.