Python & FastAPI Tutorial: Create an ai microservice to extract text from images.

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome to deploy a fast api microservice for our try django project now the goal of this microservice is to have a way to extract text from an image now we're going to be using a third-party library called tesseract to make that happen but the general idea is it's actually a machine learning algorithm that's going to extract data from an image the data that we want now the reason it's a micro service is because it's a service that our trijango project needs that is trijango 3.2 but it is a very valuable thing to learn how to deploy anything into production but especially machine learning algorithms and that's what we're going to be doing in this one so we're going to be using a digital ocean's app platform to make this happen because well it sort of simplifies the process because we can use a docker container in a docker image don't worry if you don't know about docker don't worry if that like sort of scares you it's just really one document i'm gonna show you exactly how to do it everything is gonna be done step by step my name is justin mitchell so let's go ahead and get started [Music] all right so let's go ahead and talk about the requirements for this project first and foremost the actual technical requirements will always be listed at the github repo including if we have to update any of the code in the future if you guys do as well now this github will be linked in the description so check that out for all of the code you could probably learn how to do the entire series directly from the code if you've done a number of python things next we're going to go ahead and use vs code so visual studio code it's free open source you can absolutely use this for all of your projects i highly recommend it for this one we will be using several extensions in here that i think make actually deploying our project really really easy next thing we're going to be wanting to have some experience with python so if you haven't installed python on your machine or you have no experience with python definitely take a look at our 30 days of python series and up to maybe day 15 day 16. that also shows you how to build a web app too so perhaps you don't even have to go that far but the general idea is you want some foundation in python like do you know how classes work do you know how functions work um do you know what decorators are even if you don't know how they work do you know what they are you know those sorts of things are kind of the skill requirements here and then also make sure that you have git installed on your machine and then lastly i worked with digitalocean on this one to bring it to you guys so they definitely did sponsor this series so be sure to sign up with a new account on digitalocean so do dot co slash cfe dash youtube check it out there and then go ahead and get into your console this is where we're gonna be working when we go into production so now let's go ahead and actually set up our environment now let's go ahead and set up our environment now i'm already in vs code to do the setting up for this project um the main thing here is setting up vs code and also our virtual environment so first things first i'm going to go ahead and open up this explorer here go to open folder go into our dev folder i'm going to create a new one and i'm going to call this ms fast api and django or really micro service with fast api and django so let's go ahead and put it in there and then i'm going to go ahead and initialize a virtual environment so let's go ahead and open up our terminal with control tilde and you can also see up here the shortcut for it depending on what system you're on which really shouldn't matter for you so i'm going to use python 3.8 here and we'll use the module venv and period now the reason that i'm using 3.8 versus any other version of python is because i know everything i'm going to be doing here works with python 3.8 and if it doesn't it's really easy to go backwards so like 3.6 so let's go ahead and activate our virtual environment source bin activate great that's what we want next thing we're going to go ahead and actually save this file save workspace as and again we'll do ms fast api django okay cool so next thing i'm going to go ahead and make sure git is installed and again we have get installed here this is the version i'm working on of course you can use a later version get the actual tool itself doesn't change a whole lot so you should be fine with a later version or even an older version so i'm going to go ahead and initialize my git repo here and so this is of course going to be for production now one of the things that vs code has built in is source control it actually has git built in which is nice because then we don't actually have to know that much about git to even use it but as we see we have all of these changes these of course are things that i do not want to have in production so what i'm going to do is i'm going to do a quick search on google for a git ignore file and python i literally do this all the time almost for every project i have this is the get ignore now you can also go to the github git ignore it's probably just as easy to go here and find the the python one i'll copy that and we're gonna add in a dot git ignore file here and we'll paste this in save it and no surprise what will happen with this repo especially if you hit a refresh here it's going to start to ignore a bunch of things it did not ignore everything but it ignored a lot of things so anything that's green it's going to not ignore anything that's gray it will ignore which i'm going to add in bin and include i do not want those in here in fact i really only want pretty much one file we could have the other files in here i'll leave those in just for future reference but this is sort of the baseline thing that i want and then for mac users we're going to do ds store on here as well that is certainly not something we need that's only for mac users if you're on windows or linux you should be fine you can also include it too it's not going to do anything if you include it so now that we've got that we're going to go ahead and do our initial commit with git add dash all git commit and initial commit no big surprise here at all hopefully you've done some of these things now that puts us together with our virtual environment and our git repo but our environment is not complete yet what we're also going to get is an extension for docker now even if you don't have docker installed on your machine install this in here this will actually help us with a number of things it might require docker to be installed so if it does require docker to be installed you can ignore it but we actually will create a docker file later and that's what this is going to allow us it's going to help us manage that docker file later so we can manage the environment we're going to go into and that's pretty much it that's pretty much setting up everything we need with the environment there is one other aspect that you probably want to do and that is logging in to your own github account and then you can either fork the repository that i have or you're going to go ahead and create your own right so if you were to log in to your own github account and let's see here you would come in and create a new repository and then you're going to call it whatever you want so in this case you could call it ms fast api django just like we did and then you can come through and create this repository now i'm going to end up deleting this one because this is not the repository i will use so everybody will have access to it which you'll see in a future video but the general idea is you want to make sure you have this and then we just need to add our remote and then push it right so i'll go ahead and just show you what the commands are without doing it if you've never done this before there's that first command and then it would be get push origin main so there's a chance that main is not where you're on which you'll see right here if it says master then just change it to get push origin master there are ways to change the branch i'm just not going to cover that right now so that's our environment we should have a git repo set up we should have a virtual environment set up the get ignore setup we should have all of our vs code stuff that we really need at this time set up and then we should also have a actual github repository so we can push our code into production which is something we're going to do very very early in this series now we're going to go ahead and implement the foundation of our python microservice building a really simple fast api application with a jinja template now the reason we're going to build this microservice is to offload any sort of demand or load on our primary application whatever that is it doesn't have to be django ours is geared towards django for a much bigger project but this one is just about focusing on one thing and that is doing ocr we'll get into the technical aspects of that later but the general idea here is i just want one single service that's going to do ocr for all of my potential services so that doesn't mean just one single project that could be thousands upon thousands of projects which is why we're going to be implementing this as a microservice you could think of it as a much bigger service as well it doesn't have to be a micro service per se but in our case we're doing it specifically for one project that might grow into multiple projects that end up using this so without further ado let's go ahead and get this started first and foremost i want to make a folder in here called app and inside here we'll go ahead and put an init file so two underscores init two underscores dot pi and then we'll go ahead and add one more file in here and we're gonna call this main dot pi this is a very common way to structure a fast api app it's not always in the module app sometimes you'll see server sometimes you'll see source sometimes you'll see main.pi and the root of the project that part doesn't really matter you just have to remember where your main.pi is and so before i even configure main.pi let's go ahead and do one more thing which is creatingrequirements.txt now notice this is the root of my folder now i could put in the app or it could put it in this root folder again you need to recognize where you're putting it for the future now what i'm going to be doing is using fast api i'm going to be using g unicorn and uv acorn so g unicorn and you you viacorn can work together and we will be implementing that when we go into production not when we're going locally and i already have my virtual environment activated so if you don't have it activated you're going to use source bin activate if you're on mac or linux or of course if you're on windows it's script activate now you don't actually have to use the built-in virtual environment manager like i did you can use any virtual environment manager but i think it's really important to have a virtual environment specifically just so you can do pip freeze and nothing's in there it isolates your project to some degree from other projects on your system so now that we've got that i'm in the root of my project still where requirements.txt is now i'm in the root of my projects configuration folder the main folder it's not actually in the app itself so this is a little bit different than what i might do for something like django now i'm going to go ahead and do pip install r and requirements.txt okay so if we recognize this file here this is actually working towards what's called a docker file so i'm not going to actually save this just yet but at some point in this docker file we are going to need to run this command we do it locally we're also going to have to do it in production in production we're going to be running off of this docker file this will really configure our environment for us in a sense it's not actually configuring our environment the way you are doing it on your local machine but it does it in its own way again i'm not going to be doing that just yet but notice that i have some syntax editing or showing up the syntax for the docker file itself so it's going to show me hey this is invalid this is not a valid docker file so that's another reason why i wanted you to install that on your local machine if you could okay so now we have our actual environment set up so i'm going to go ahead and do pip freeze and there is all of the dependencies for this project at this time so let's go back into main.hi and now i'm going to go ahead and actually initialize my fast api app it's really simple this is another reason why fast api is blowing up is you can do fast api like this declare your app call it fast api and then do a decorator with app.git so git is the http method of git and then we can define home view and then we can return a dictionary of whatever we want so i'm going to say hello and world okay so this is a fast api application it's really simple so to actually run this application we need to reference this single variable right here so every once in a while you might see it as server but i think from the documentation and also best practices it's good to call it app but notice i have a folder right here or a module right here that's called app so really i have app main and app so the path to this from the root here is app main app right so that's actually how we get to this module itself or this variable so the reason i say that is because i want to use uv acorn to actually grab that so app.main and then colon app so app is that this main module right here main is this main module and then app is that variable and then we can do dash dash reload to make sure that every time i save this it actually just reloads what's going on okay so i have it running and if i go here i see hello world going now you might not have a json parser on here you might see it something more like that but your actual chrome can download an extension to show json a little bit better so that alone right now is a rest api endpoint it might not feel like much of anything but it is a rest api endpoint so as it stands i could totally release this but for now i'm going to go ahead and do a little bit more just so we have at least a somewhat functioning website or something ready for this okay so i'm going to go ahead and call this home detail view and then this is just our simple home view okay so these are two different http methods so http get and http post now if you're not familiar with these don't worry we will cover them again a little bit later but the general idea the get method is usually what you see when you go on to a page the post method is when you want to send data to any given endpoint so in this home view i don't actually want it to return any sort of dictionary like this i wanted to actually have html being returned so to do this we actually want to implement what's called a template a genji template and that's something we'll do in the next one but if you have any questions on this so far let me know otherwise let's keep going it may come as a surprise to you that this is a full-on web application in fact it's a full-on web location that is a rest api rest api is a incredibly common way for two applications to communicate so app to app really like it could be an ios app to our web app right it could be a web app to a web app and so on right we could go down the long list but basically a rest api is how software communicates with each other typically through the internet and it's often referred to as a restful api service so what's happening with fast api is every endpoint by default will return back something called a json json is javascript object notation now if you're familiar with python what you can actually look at is it's a lot like saying json and then dumps and then going in here but it actually does it all on its own so feel free to play around with that with your own dictionaries as you see fit but the general idea is fast api always returns json unless you change it and that's what we want to do now we actually want to change it we want to change it to using an html template so it looks a little bit more like this or like this or like this then like this right so an html template is what you're probably used to on the vast majority of web pages html is made for us for humans rest apis json those are made for computers that's pretty much the gist of it okay so with that in mind let's go ahead and actually add in the jinja 2 package so jinja 2 is really just the package name although everyone refers to it as jenja so even if you're on their documentation you'll see it's pip install jinja 2. now if you've actually used django before jinja is not really different than the django templating engine it's just a little bit more advanced in some features in some areas so that's why we're using jinja but also we're not using a django project so that's that okay so the idea here is we're going to be using jinja as our templating engine we don't actually have to do a template at all i'm just doing it so you can have a better just an understanding of how fast api works and how you might want to explore it even more but also with our main index page the actual home page what we don't want to have happen is something like this so if some user accidentally comes here i wanted to actually just at least show something that's kind of the idea so the something i'm gonna have it show is a bootstrap template right so if you go to getbootstrap.com and then go into the introduction or getting started there should be an example in here if you scroll down this starter template and so this is actually the template i'm going to copy and i'm going to bring it into a folder inside of my app and i'm going to call this templates and inside of there i'm going to call it base.html and paste this in now the reason i'm calling it base.html is because this is going to be the base the root of all of my other html documents which we'll see in a moment and so i actually changed a few things about it off the video that i'm going to paste in here the things that i changed were i added this style right here i got rid of a bunch of comments and then i added this block content here so this block content is going to allow me to do what's called string substitution or really block substitution from one html document to another now it's a lot easier to understand it once you actually see it in action so let's go ahead and create a new template here and call it home.html this of course is going to be our for our that home page now what i want is everything in here basically i want this entire thing like this right except what i don't want to do is have to retype everything now that's what these little block things do here so we can come in here and i can just say you know something like h1 code on something like that right or i can use some actual bootstrap classes and just do something like this okay now this is great it's giving me a thing that i can replace into another file think of these as being combined together where when i actually do render it out when i end up making it work it's gonna look like that okay so let's go back into home.html there's a problem here and that is it doesn't know what other file it should be copying data from or inheriting from so we use extendsbase.html right now if you're familiar with python classes think of it like this as my base class right and my base class had let's say for instance this data and that data and then custom data now if you create another class called your home class and just leave it empty and say you know custom data equals to code on now you still want this data and that data in this home class when you initialize it right and so if we did something like this and then obj dot this data or that data it's going to give us nothing it's not even defined on this home class so hopefully you already know what the solution is that is to bring that base class in that's essentially what's happening here it's not exactly like that but it's close so this actually overrides what's inside of this block right here much like would happen if you're doing the same thing using class inheritance and if this part is complicated for you with python then this is probably not the series for you okay so now that we've got these template things definitely can talk all day long about these templates there's a lot to it i don't want to do that because that's not really the point of this series if you want to see more on jinja templates let me know in the comments and we'll take a look at it okay so now what i need to do is i need to actually get that template i need it to be the response here instead of this dictionary here so in my opinion this is where fast api and flask start to get a little bit more complicated because it's not actually working the way you might expect like i can't just say return render the home dot html you know with some context or something this actually does not work for fast api there's several reasons for it but the main thing here is we right now fast api has no idea about these templates right there's nothing in this application that's referencing that template directory so we need to change that so i'm going to go ahead and get rid of this right here and just leave it empty dictionary for now and now what i want to do is i want to import two things first of all from fast api dot responses we're going to import the html response now the reason we're importing this html response class has to do with the get method here so what we do is we have to pass in the response class in here right here so this actually overrides this response class so it gives an html response now so what is an html response well it's a string like hello world so we save that and then let's go ahead and take a look at here and what do you know it's now showing me hello world it's showing me actual html so i mean i think it would even error out if we tried to send back a dictionary it might not but let's go ahead and try it out let's see looks like it's still running and now if we go in here now it does error out because it's not expecting a dictionary as a response it's expecting a string the html string okay and so what jinja actually does is it converts these files into an html string but it does it using like almost like strings formatting this is something i'm not going to get into right now but the idea is we want to get these files or one of these files and then turn it into a string and just return that that's kind of the gist of this so the way we actually do that is first off i need to set up the request so whenever you need to reference the request on the view we come in here and say request is equal to that request class or of type of that request class and in here we can actually print out what this request is of course so it's an instance of that request class we can print it out we run in here and we see that there's this request class there's a lot of information that's built into that something i'm not going to cover but the idea is we need to actually bring in the class type itself and then set that equal in here based off of the type that's coming in with that argument okay quite a bit different than django if you've used django django just infers that it's the request class and here we have to actually declare the request class type okay cool so now we need to actually get the template response set up so we're going to do from fast api dot templating we're going to import the jinja 2 templates okay and we're also going to import half lib i don't need that json anymore so import path lib here you can use os if you're more comfortable with os i'm going to be using path lib and so what we want here is our base directory this is really the directory where app is so we can grab that template directory so this is going to be pathlib.path two underscores file and then dot parent so this of course is gonna give us the path to this file this gives us that parent path next i'm going to go ahead and say templates equals to well the jinja 2 templates class and then the directory where we have it and this is really just going to be the string of the base directory slash templates now naturally we can and maybe should print out what that base directory is and it should be app templates you should get the exact same thing that you have set up locally right and you can also print out if that exists as well which is cool so we could do dot exists just like that and that's a reason to get more familiar with path lib if you aren't okay so this is a ginger two templates instance the class itself and so i can actually come in here and do templates dot template response and this is going to take in which template i want which is home.html now this right here the path that you would put in here is relative to the template directory so if you had it in a folder called abc you would put you know that in there as well okay so we've got home.html in here and these template responses require you to pass in a dictionary that at least has the request itself coming in you can also pass in any other argument that you might want right and so that argument itself can be accessed in the template that you're rendering just like that okay so we refresh in here now and what do you know i actually have some data that's coming through in here and it's rendering out that template context okay so just a recap as to what this is doing right here is it's basically opening the home.html file and then what it's doing is turning that into a string whatever's in there so let's just arbitrarily say abc is in there and now if we did abc like this i can actually call dot format abc equals to one two three and that would actually change that item here very similar to like the f substitution as well where it does change that variable or it can change that variable too so it's really just combining a simple html document with some string substitution and that's how you get a template response so of course we can also look at the request in that template as well but that's pretty much all i'm going to be covering with the jinja 2 templates but it's nice to see that hey now i actually have some sort of way to actually render out human readable responses right and that's the key so now that we've got this it's time to actually move into testing out our project then going into git and putting it into production whoa now very soon we are going to be bringing this application into production now before we do that we want to actually flush out testing to ensure that it's working as intended so that when we go into production we don't have any bugs or errors related to the code itself so to do this it's actually going to be really simple first in requirements.txt we need to add in pi test and requests as well so we'll go ahead and do open up a new terminal window you can still have your server running that's fine if you wanted to we're going to go ahead and do pip install r requirements.txt and we'll just let that run you could also upgrade pip if you'd like that's completely optional especially in development okay so now that we have pi test in here what we should be able to do is write out just simply pi test and this should test all of our python code now in our case it's actually testing a lot of the third party code now you may or may not want this to happen right so i'm going to keep things simple and remove it for now so in the root of this project this is where i will be doing my tests so i'm going to make a new file in here and we're going to call this file pytest.ini and so this file itself is for py test and the idea is we don't want certain recursive directories so no recurse durs equals to well certainly lib that's going to be the main one as we see right here and if you put that star there and then run this again we should see no test now the other one i'm gonna also include are bin and include these are of course all of my virtual environment files themselves just to make sure that i'm not running tests that i don't intend to run at this point okay cool that's just a simple and easy way to do it so next we need to actually create a test now what happens when you create tests as you call it test underscore and then some test name in this case i'll just call it testingpoints.pi and the goal of these tests are well simply to test the api endpoints that i have so this is not going to be really that complicated the first thing i need to do is i need to do from app dot main we're going to import app okay so this is coming from the app module the main module and then app that actual variable okay and then we also want to do from test fastapi.testcli we're going to import the test client now what this is going to do is we are going to create a test client for this app and now we can actually treat this just like python requests so this is very similar to request. almost like request i get or really just requests but it actually is going to have our app run right so let's go ahead and close down our app so we can see this in action okay so i don't have the server running at all and so i want to go ahead and test one of my views and again we'll call it test underscore if we do not call this function test underscore it's not going to test it we'll go ahead and call git home okay so we're testing the get response from home so response equals to client.get and we want to go to that home page okay so this is now really the requests.get equivalent in fact the response the actual response class itself is a lot like that that's the python requests package that we installed just a little bit ago and so what we can do in here in this actual test function is we can assert that the response status code equals to 200 right so 200 is okay good to go that's what we should expect from it if the server was down if it was an invalid thing that we would see a different error so now we can run pi test and hit enter and i get this warning this has something to do with a third party package i can ignore that for now but what we see here is i don't have any errors or it seems like i don't have any errors so let's actually change the test to being let's say 500 right so i'm going to assert that it's a server error now and i should get something like this this is not a correct error right so it's a 200 status code okay so changing that back to 200 that's all we really need to do now we could break this view as we might want to but it's not really i don't think it's great to just break views just to write better tests but what would happen over time as you write tests is you'll get better at doing it and therefore you won't have to break your current existing views accordingly so the other thing i want to test is response let's do response.headers of content type we want to set that equal to text html now i think this one's actually important because of our git view we wanted it to be html text so let's go ahead and run our test again and this time i get something different right so it's asserting that it's text html but it actually has a few other things in there so what i want to do is just say assert that text html is in response headers and now i run that and sure enough it is right so we can actually take this same concept and bring it down here and now test the post home and instead of using the get method we'll use the post method just like that and they are very very similar tests that would be doing but now it's going to be application and json as the content type and we can again test it sure enough that's true okay so that's nice the other thing is i can actually test the response from there so i can also assert that the response dot json is equal to that dictionary literally the dictionary that was written there now this isn't always going to be practical but we could at least try it out and sure enough there it is it does two tests and we have no issues so that is pi test now one of the things that we might also test is the actual template response itself so what's coming back in here so i will let you take under that under your wing how do you actually create a string but i will at least give you that you can assert response dot text is not equal to you know something like h1 hello world right so we save that and we can run it and of course that still runs so us response.txt will at least give you the html response now i'll leave it to you to see how you can actually render out a template with the correct context and all of that specifically for your tests now if you do find out that answer let me know in the comments i would love to see your responses but the general idea here is we want to test our code to make sure the integrity is there and that when we actually bring this into production we have a way to run these tests before we do anything meaningful with git and version control i want my code to test i want it to happen automatically and the way we're going to do this is by using a package called pre-dash commit so we're going to go ahead and install that and we'll do pip install dash r and requirements now what this package will allow me to do is to define a bunch of hooks or steps that i want my system to take prior to doing a commit otherwise known as pre-committing and so what i'm going to do in here is in the root of my entire application where git also is i'm going to go ahead and add in a file called dot pre dash commit dash config dot y a m l so this is a yaml file you can also use y ml but yaml is yaml 8 markup language it's like recursive and of itself of a name in this case we're going to be using yaml as a configuration file for the pre-commit package because that's what they recommend so let's go ahead and take a look at pre-commit now with dashes help and what we see is all of the different commands that we can end up using and so the one that should be interesting to us now is this sample config one so it produces the sample pre-commit yaml file right so right there so let's go ahead and take a look at that with simply sample dash config hit enter and i'm going to go ahead and copy this from repos down you could always leave the comments in if you'd like but i'm going to leave them out okay so this is a actual pre-commit file now and i can use it so to use it we just do pre-commit and install hit enter and it installs it for us notice the location it installs it for us we'll come back to that and then i'm going to go ahead and run pre commit run dash all dash files and hit enter and so since it's installed what it would actually do is run all of these things in this case the fix into files thing failed right so i'm actually going to get rid of that one because it's failing on one of these files so i'm not going to really worry about it in this case so let's just go ahead and get rid of that i just really want them all to pass because well this is just an example i really just want high test to run but it's nice to have some of these other things as well and so now that i've run it actually seems to fix and run just fine so let's go ahead and clear everything out and run it again cool we're good to go and so this is already installed on our machine so now if i actually run something like the actual commit with git so git add all it's adding all the file changes then git commit with the message of you know fast api get and pre-commit i hit enter it actually runs all of those things and it's going to do it every single time i commit it which is really nice it actually ensures that hey at least these things are being handled now another thing that i can do is add in for pi test and so what i'm going to do here is i'm going to literally copy this same thing here and right underneath and i'm going to paste this in and so the repo is no longer an actual live repo somewhere else it's really just our local machine right just like that the next thing that we want to actually do is get rid of these ids here and instead we want to have a pi test id and this is going to be pi test we'll just call it pi test check we'll give it a name so tab over a few times to be right under the id like that and then i'll go ahead and call this the pi test runner and then we'll give it an entry so this is simply pi test as in what is it that i want to call locally which is simply just pi test right there you could add other things to it but that's all i want to call and then we'll go ahead and add in the language in this case i'll just use the system itself and then we'll go ahead and do something called pass file names and we'll go ahead and say false and then always run being true this is an example that i found that's just really simple to run our pi test and so let's go ahead and give this a shot so first of all i could absolutely run it with git commit or what i actually have to do right now is i have to install it because i made some changes and then i want to run all files and this should actually run everything for me including our pi test runner which is awesome which we'll test in a moment as well now if you ever get into a place where you're like hey well let me look at this command here oops we can cat out this and take a look this is all of the things that are happening in our pre-commit so you know the pre-commit package made a pre-commit hook we could have manually wrote these things out that would run some sort of script command for us but of course i don't want to do that right i just want to run it based off of pre-commit and that's why we're doing that here okay so if we did want to get rid of those we could just do pre-commit uninstall that's one way to get rid of that another way would be let's install it back and then cd into git and hooks then remove the pre-commit file that's it now it's gone now it will not be able to execute those hooks at all so we go back in here but of course i want it in there so pre-commit install and oops spell install correctly and there we go so now i have that all in there it's all ready to go and i'll do that final commit that we're going to do here and that's gets add all git commit give it a message of some kind and then in my case i have a get remote already built in here so get remote dash of v my remote is on this repository yours is going to be likely on your repository now if you have it on my repository that's fine but it's probably not going to necessarily push it like you might want it to because you're not a you know collaborator on this per se you might be but you might not be so make sure that your local git user is right here not mine that's kind of the key here what this is called is completely up to you and again if you need to add one you do get remote add and then the name you want to give it origin is very common for the root remote that you would end up using like on github or bitbucket or gitlab you could also do something like abc or my server you know i mean you could you could name it all sorts of things but any case we now have this remote here so i'm going to go ahead and do git push origin main and run it right so in this case it says everything up to date now in my case i have i'm actually working on a branch so i'm going to go ahead and do git checkout main and then git merge 7 end and now i'll go ahead and do git push origin main hit enter and so now all of my new code should be on there and if i go on the repo itself sure enough it all is and it says four minutes ago is when i did that commit because we've been chatting for a little bit but the idea here is now it has this actual pre-commit config on here as well so if i ever needed to rerun this anywhere else i could do that and also the fact that you know hey where's pre-commit on our requirements uh-oh we should probably have it in there and save it you might already have that but there we go we found a little issue so we did get status get add get commit updated requirements get oops notice it ran everything so git push or germain pushes it refresh and now it has those commit things right pretty cool okay so the reason that we do this or the main reason we're doing this now is to a couple things one to make sure our code is well tested even though it's teeny two um to make sure it's tested when we actually try to push it into github and then three to make sure it's ready to actually go into production so all of the related tests that i would need to do could happen locally they can also happen on github so you don't actually have to run pre-commit on your local machine you can actually run it with github actions as well as well as a number of other things too so it's certainly not something that has to happen but it's really nice that we can do it locally and get in this habit of constantly testing our code and hopefully that also means that you're constantly writing tests let's go ahead and get this thing into production now let's go ahead and jump into digitalocean to go into production so do.co cfe-youtube is where we're going to be and then once you log in and get your account set up navigate over to apps or simply cloud.digitalocean.com apps now in here we're going to go ahead and create a new app and we're going to click on github now if yours has a little arrow like that that means you're going to need to integrate your github account or if you click on github and you're like wait a minute i don't see the code anywhere now my code is right here or at least it seems like it's right there but it actually is not because i'm actually not working off of this user i'm just a collaborator there in this case but i am working off of the teamcfe user so this is actually my user right here and if i look at my repositories i see that this one was updated two hours ago this is the one we actually talked about at the beginning so i'm actually going to delete this one so i don't get confused you might already have all your code on here but i'm going to go through this as if you don't already have your code on here perhaps you wanted to skip a few steps and you just want to you know copy what i'm doing here just to go into production which is totally okay so at this point we can pretend like we have none of our current code on here so going back into the coding for entrepreneurs repos or the github profile we go into the repositories here and we want to go to the fast api micro service as i'm recording this this is private by the time you're watching it it's not going to be private and so what i've got here now is i want to actually fork this if i go to fork it what this is going to do is it's going to bring it over into my account as you see it now says teamcfv over here so it's going to take a moment for that to happen and it also tells me where that origin is which means that i can actually update this later as that code changes which i think is great so going back into digitalocean well that's not necessarily going to show up right away so if i refresh in here it might show up right away it might take a few minutes for it to actually get that new repository in my app platform account right luckily it happened really fast in my case the branch we're using is simply main that's it there are other branches but none of them are deployed at this point if you see a branch called production that means that i did that sometime in the future you could totally use that one as well and then also we want to check auto deploy code changes now now that we since we have the pre-commit setup right i think it's safe to say that we can go off of this main branch and auto deploy those changes because i'm not going to be doing some dramatic things out here that aren't tested and aren't ready and so when we go here we see that it detects a python application so at this point we're doing a python application if you see main and it's detecting a docker application then you might need to change some things in there so feel free to well maybe use a different branch uh the other branch option would be the branch four seven so get checkout branch or let's say seven end so if you see seven end in there you want to copy exactly what i'm doing that's where it will detect a python application okay so now that we've got this there's one thing i need to change which is the g unicorn command i need to actually use uv acorn i can use that worker temp directory as it put but i want to use the dash k and it's going to be uvacorn dot workers dot u viacorn worker and then i want to use app.main colon app and i don't think i need to bind it to anything but that's kind of the idea here is our main app and we want to use the source directory which is the root folder same as what we've been doing so far okay so go ahead and hit next i don't need to add a database on here we're not going to be storing any data with this microservice it's just really going to be running functions for us you can choose a region any region you'd like whichever one's closest to you is usually what i recommend i'll just leave it at new york at this point and then we'll use basic and i'm i'm literally just going five dollars a month you can do whatever you like because you probably have that 100 credit so you could probably put a pretty hefty machine on here but five dollars a month should be plenty especially as we're testing out a little app like this and so what it's going to do is it's going to build this it's going to take some time to actually build this it happens every single time it's gonna take some time the reason it takes time is because it actually builds a full on docker container which really helps isolate everything as much as possible but where we stand right now it should be able to find our requirements.txt and let's see here so it we should see requirements.txt in here somewhere when it actually starts installing notice it's actually using python 3.9.4 instead of python 3.8 we haven't done anything that should not allow python 3.9 to run but in case that you wanted to specify which version of python you would just change runtime dot txt and change it to python python-3.8 let's see what actual version it is with python version and then 3.8.2 and then you would commit that runtime and send that in and it would would actually change the python runtime but notice that it is installing everything that it needs to install right which is great that's exactly what i was hoping for and hopefully you are as well now in the case of this particular application the installation process should not take that long because there's not that much to it there's not that many requirements and there's not very many integrations but it still can take some time so i'm gonna let it finish and we'll come back one thing i did want to mention though is if you were to push this into production let's say if i did get status and then get add and then gate commit and then updated python runtime and then we pushed this into production like git push origin main or at least pushed it into the repo digitalocean will automatically try to rebuild this so it would actually cancel this deployment and then automatically rebuild a new one which may be something that you want this is another reason to be testing our code frequently right so we want to make sure it's well tested so i don't have to cancel deployments while they're happening because this build time it just takes a few minutes that's you know it's not super fast to get back up and like fix a bug that may have been avoided because you ran some tests anyways let's let it finish okay so after a few minutes it finished building or maybe 10 minutes total i don't know it definitely took some time to finish building so refreshing here and it's certainly possible that my run command is incorrect so let's give it a shot i think it will tell you if the run command's incorrect and what do you know there it is it's actually working correctly and it says codon not really that exciting of an application but you know what it is in production to me this is one of those things that's like so so exciting so i can just keep going so naturally the next thing is we actually need to set up our environment a little bit better to actually use the ocr machine learning algorithm that we need and that is why we actually need to use docker i'll get into that more but for now we've officially deployed our first fast api microservice for django on digitalocean one of the downsides to deploying code like this and the way we just did is the lack of being able to control the actual operating system environment we have the requirements.txt and this sort of helps us with the environment but it doesn't actually do it on the operating system level this is just python packages so what we're going to do now is we're going to deploy a docker file just a docker file to app platform to actually handle this now if you're interested in learning docker i highly recommend it it's a really really interesting tool but it's outside the context of what we're covering here i do have a full series on it where it really goes in depth on how to use docker and another tool called docker compose but really the key thing here is we're going to be deploying to at platform again in a very similar way to what we just did except we're going to add another file to it actually two new files now we actually already did create a docker image that is exactly how ad platform works when you just deploy code it actually creates an image for you as you can check by the logs of the project you just created now i did some tests so i actually had to rename the project but the idea here is we want to re-engineer a lot of the things that build into this docker image so let's go into that project that we just created which in my case now is just fast api dash ms but yours might be fast api microservice and all that but anyways we want to jump into the settings and go to this component i'm going to scroll down here and look at this run command i actually want to keep this run command and i want to bring it in locally so in my local project i'll go ahead and create a file called entry point dot sh so i'm actually going to paste this in here now the reason for this is because i actually want to make sure that when i use docker it's going to still call this same command that's actually why we have the command in here in the first place so this is actually going to be called inside of a docker image but i like doing it in an entry point dot sh like an actual shell script so i can test this locally if i needed to so the other thing is i want to actually put in the hashtag exclamation mark bin bash this is just telling the shell that this is a bash script so you can run it as any normal linux script the next thing is we're going to do slash usr local slash bin and then g unicorn so hopefully what this tells you is like we're going to be installing unicorn on the local user inside a docker you might have no idea what that means but basically this is going to be system-wide it's not going to be a virtual environment like what we did here now you totally can do a virtual environment it just takes a few extra steps something i'm not going to cover right now next i'm actually going to bind this to a specific port so the port i'm going to bind it to is we're going to declare it as a run port and it's going to be just like this and i'll explain this in a second and then we'll grab that variable and do bind and 0.0.0.0 colon dollar sign curly brackets run port okay so this variable here i can actually use at any time i can change this variable based on another environment variable of port this environment variable i believe at platform will actually set for you if it doesn't set it then we get 8000 as our port right so this is kind of giving me a backup port and this is just a way to do that string substitution to bind it to 0.0.0.0 now this actual ip address is just so it can attach to any other ip address that's really the key thing there okay so that's our entry point now unfortunately i can't test this locally in my current environment because i don't have g unicorn installed in my local system right and if you're on windows it certainly won't work because that's not how the windows file system works this is specifically for our docker file so we can test docker files locally and do all that stuff but again that's outside the context of this one and that's why i recommend you check out this project if you're interested but now that we've got this we need to create our docker file so this is going back to what we saw before now this takes a few different components to it so first off we say from i'll come back and fill it out then we do copy then we do run and then we do command okay so the first one the actual easiest one for us right now is command the very last thing that we want to run is entry point.sh right so that's that file that i literally just created no big deal here so what are these for well this first one is from now this is kind of like saying what other pre-existing docker file or docker image do you want to use that's probably incredibly confusing i think of it more in terms of hey which operating system do you want or which programming language do you want in our case we want python now docker has official python images it has official node images as well and many many other official images but we're going to go ahead and stick with python now what is the version of python we're using in our virtual environment and that's 3.8.2 in this case i'll just go ahead and say 3.8 now i can leave it like this but one of the goals of using docker is to make it as small as possible as in you don't want too big of an operating system in here so think of this docker file as creating our operating system so let's just do dash slim this is very common as well there's many other ways to optimize docker files but i'm just going to leave it as is next what are the actual local files that i want to bring into production now the files that i want are for sure that entry point dot sh and i'm just going to put it into the root of entry point dot sh so the actual root of the docker image itself that's completely fine next what we're going to do is we're going to go ahead and copy well i want to grab the app itself so app into slash app okay so the local app that's this folder right here into another folder called app now if i said abc here then all i'd have to do is in my entry point i would have to update this right here from app.main to abc dot main but of course i'm gonna keep it in as app okay and this also in as app the next thing is i need to make sure that i'm bringing in my requirements.txt so copy and requirements.txt to requirements.txt now you might be like well what if i just did copy like this right what if i just copy this whole directory generally speaking that's okay and actually since we're using git it you could probably get away with it but the problem is is if you did this locally if you did it somewhere else then it's going to copy all your virtual environment stuff there are ways around this there's a file called a docker ignore file but that's not something i'm going to cover right now i'm just going to leave it in just like that this will give me the general aspect of the things that i want and then i'll also put it in copy like that now another thing you might see on docker files a lot is a working directory and it might be into the app itself like this the actual destination app and if you did something like that then yet again i'd have to get rid of dot app or app dot really in my entry point that is basically saying hey jump into this file or this folder right here again it works but it's completely up to you on whether or not you want to go that route i'm going to keep it as close to as my local development environment as possible okay so hopefully at this point it's not that complicated as to what's going on hopefully maybe it is maybe it's not now i can actually run python 3 dash m pip install r requirements dot txt now the reason i know i can run this is couple reasons actually one we are using python so i for sure have python three point whatever in there in this case 3.8 then python 3 has a module called pip installed often not always but often and we can actually run requirements.txt or at least that would be the working theory now unfortunately this won't work right off the bat not with docker files and nuts more specifically with linux files so with linux what we often have to do is set up the environment even more so it's common to do apt-get and then install as well as update so we start off with update and then we run our install commands so apt get install and then we'll do dash y to just say yes we want to install all of these now there's a few that we need okay so the first one is build essential the next one is python three dash dev and then a slash this is this slash right here is just basically chaining these all together as one big command next one is python 3-d setup tools and then we're going to go ahead and do tesseract dash ocr there is the key for this entire project this is why we have a docker file is specifically for tesseract ocr i think a few um commands that we need in order for tesseract to work are make and gcc so after we do all that then i can run this python 3 m pip install requirements.txt now if you're familiar with python you might also get away with pip 3 but i'm just going to stick with python 3-m pip install so all of these commands right here if i were to actually go into a linux virtual machine or a linux machine of any kind even a raspberry pi if they were all run by themselves you would absolutely get this stuff installed correctly and it's literally you just don't have run in there you would just run the commands like that and often you'd have to do it as a pseudo user but in this case this all should work now there are a few other commands that i'm going to add in and just copy and paste these and these are pruning commands right so i'll put another slash here and chain it together with these so simply removing the things we don't need after we run all the installations then removing anything else and everything else that we might need to prune again to make this image as small as it possibly can so that's really configuring our environment now in some cases you might need other packages it really depends on what system you're working on this is a way to do it right now that we have this there's one last thing is i want to make sure that my entry point is executable so i'll just do run and show mod plus x entry point dot sh okay so we now have our official docker file it's ready to go so what i'll do here is i'll do git add dash all git commit dash message and we'll just say nine and i'm only calling it nine because this is part nine and it's deploy docker app to do app plot forum okay and in this case it actually gave me it gave me a failure on my trailing white space which is kind of funny so i think it fixed the docker file so let's go ahead and try that again oops i need to add it and now run it there we go okay cool so it fixed my docker file for me yes love those pre-commit commands okay so um and now what i need to do is actually push this in to uh my origin so i'm gonna do it get push origin main now i want you to notice that i'm using nine dash n i actually want you to stick with that branch too if your local branch is different and you pushed it to your origin that's fine but now i actually want you to follow along with me so you have exactly the same code as me and there's no issues whatsoever there might be issues because that's why i'm recording this the way i am because there were issues on my end okay so i'm going to jump into my project here and i'm going to look for branch 9-end okay so notice it's not there yet so let's go ahead and push it in so git push origin 9-end okay so now in here i should have it and there it is nine dash end and so on my local project or my local user where i forked this already right so if you hadn't forked it fork it now now you should um fetch upstream if you didn't already like if you just forked it you won't have to do that if you forked it before you'll have to do that but you can do this at any time if it needs to be forked next we're going to go ahead and scroll down we just want to verify we have 9-end in here and it looks like that part did not come let me just refresh in here and 9-end yeah so i'm not seeing that branch coming through which is odd let's go back yep sure enough it is definitely there okay and so nine dash end nope still i think maybe the caching issue is in here perhaps that's it so let's search here too no okay so i'm not finding the branch really simple solve for me at this point you will definitely find the branch because of where you're watching this you're not watching in real time like i am i'm going to go ahead and delete this and go back in to that branch or that project itself and i'll fork it all over again okay so this actually won't break my other application right so the reason it's not going to break it is because the repo itself has not actually changed the source repo even if it did break it we're learning you could always delete things you could do a lot of stuff to bring us back to where we need to be but now if we look in these branches we should see it and sure enough there it is now this is all because of now what i want to do is go back into ad platform and we're going to create a brand new app here i'm actually not going to add anything to my other app because we don't want to cobble things up in there you could totally delete the other app but i'm going to do it in here so fast api microservice for django find the branch that you want you can certainly auto deploy code changes if you like i am not going to be working on this branch any longer in fact i'll be working on the main branch so depending on when you're watching this the main branch will have all of this code as well so let's go ahead and now hit next and so this is gonna detect a docker file so it's going to automatically go to the docker file it certainly detects everything else but it automatically goes to the docker file and so we've got our http port in here right and notice that it says your port env var is being overridden by this setting which means the environment variable port whatever you put in there this setting is going to change it and that's also why i did this so i can actually go off of whatever that port is and place it in here but if i need to test at any time i can decide whatever port i want that is a critical feature which is why i put it okay cool so now that we've got that let's go ahead and go to next and we're just going to make this you know fast api and docker okay and next and then come in here five dollars a month launch basic app okay so it should just take a moment before it actually creates it while it does that i'm gonna go ahead and search fast api docker and what i'll see is there is an actual guide to deploy with docker on fast api's documentation but what you'll see is this right so this looks nothing like ours this is because the creators of fast api have their own docker image their own image that you can actually copy from to shortcut all of the things that i wrote here this right here is like the longest it probably will ever be for you when you're deploying python applications unless of course your operating system needs more than just the tesseract ocr but the cool thing is now that you have this you could deploy it to docker hub and have your own version of this but you could totally work off of theirs as well so instead of using the python that i did you could totally do that exact same thing and it would still work roughly the same it's a different version of python which still should probably be fine where we're at but the general idea is docker is incredibly flexible with all of those things yet another reason to learn it but anyway so now what we're going to do is just take a look at the build logs so i've had experiences with deploying my first docker images to app platform where the very first build does not work right and then the second in every build after that does work so when in doubt especially when it comes to app platform you're going to want to every once in a while force build rebuild and deploy and clear the build cache this is really good to do from time to time it takes longer to do the build but it's really valuable when you're running into issues related to the build i'm going to say it again once what another thing would that would be really valuable is to actually test out the docker file locally and learning how to do that would be really valuable as well but you don't have to right so what i did so far is i think fairly straightforward it's just another file it's just like requirements.txt but now it's for the operating system it's almost like os dash requirements.txt and it's just in a certain format that might seem a little weird at first but i think it's pretty intuitive once you actually start using it and docker does get more complicated than this i think this is like on the easier side of things but i would also argue that like 90 of the things that you might end up doing is related to this unless you really get into you know managing docker deployments maybe it'll be a little bit more complicated than that the other cool thing about docker while this is building is it actually will set you up for using something like kubernetes or many other services that really run off of docker because of how it isolates the environment better than anything else and it's really flexible it's not stuck to just python it can be anything requirements.txt is for python package.json is for node.js packages or javascript packages so that's another cool thing now i i do know that these builds will take anywhere from five to ten minutes maybe even 15 minutes it really depends on how long their service is running so i'm gonna go ahead and pause this and we'll come back once it's done okay so six minutes of build time it's actually a little bit faster in my experience than the other way of deploying um and if we look at our details here i don't think i see anything that really breaks what's going on as far as the build logs are concerned but one of the things that you can notice is if you scroll up a bit it's going to tell you that a bunch of packages will be removed it's going to tell you that some packages should be removed right so there's a lot of those commands that are sort of necessary for it but it also shows you that it's installing everything that you need now my deployment logs it looks like everything's working correctly so let's go ahead and open this up with the correct port so i'll go ahead and open the app here and what do you know it's definitely working with the docker deployment now the other cool thing about this is when we want to use the actual deployed version of this we can go into the console for this actual project so if we list it out there is the result of everything related to our docker deployment so everything in here so notice copy app app there's app right entry point requirements.txt so certainly those things can be in different places right so perhaps it would be better in the long run to do something a little bit more like this so it's not cobbling up anything else related to the project itself or the entire right the actual environment itself but as you see it's working so we don't really need to worry too much about optimizing the docker side of things without really using everything else so the other part of this is what i haven't installed yet is the python like any particular python package related to ocr so we can actually run pip install in here so we can do pip install let's say let's do something that we definitely don't have which was django so pip install django and what do you know it's going to install things however don't get fooled by this because this is installing it on a docker container that is ephemeral that means that it's going to come and go at any time right you may or may not want to install things on here the reason i use the console is to test the installation test things out in a actual docker container versus trying to get it locally all the time this is one way to sort of circumvent working on it locally and so another way to think about this too is to go little by little adding things not necessarily doing it all at once the reason we're doing it all at once is because well we want to get this tesseract ocr working as soon as possible so let's go ahead and go back into the python code at this point we have two different environments one is our production environment one is our development environment what we see here and of course the production environment is on digitalocean now the idea here is we need to think about this code in two separate ways actually we need to treat it differently as you might imagine so what i want to do here is i want to have something like debug being true if it's in development and false if it's in production so how do we actually go about doing this well first and foremost i can add in an import to os and do os dot environ dot get maybe like debug and we can set that equal to maybe the string of one right so that would also mean that i should probably put this in as a string as well just like that so how do we actually have debug work well i'm going to show you one way the way i don't recommend first and that is literally writing now debug equals to that and then uv acorn app dot main colon app and then dash dash reload hit enter and in this case it will give us that debug okay and generally speaking this is fine like i can absolutely do this but then i have to remember when i am local i have to remember to switch that from on or off right or other words if i actually run it like this now it's going to say false so i want all that to happen automatically now in order for this to happen we have to introduce several new things they're not really that complicated but they are useful so we'll go ahead and import from pydantic we're going to import the base settings class and then i'm going to declare my settings class as the base settings and now i can actually have that debug in here as a boolean type and we can set that equal to false by default right so this class here is essentially doing this same thing it's just slightly different and so what i do is just say settings equals to a instance of that class and then i can just come in here and do settings.debug the nice thing about this being lowercase is it does match the debug environment variable still all caps which is typically done for environment variables but we can actually take this a little bit further and we can actually base it off of something else which is a dot env file so if you do emv file like this and then do emv this is now going to look for an env file inside of our project so if i do emv here i can now say debug equals to one right i don't actually need the string here but generally speaking this is how we would do that now in order for this to work if i actually restart it um it's probably going to tell me that i need to install python.emv so make sure if you don't have it already make sure you do so python dash dot emv just like that there is a django one just be aware of that this is the python one and of course i would i definitely want this in my requirements.txt if i don't already have it okay and so the reason for that has to do with our environment variables to actually use these now according to the documentations on fast api this is not the most efficient way to initialize these settings so i'm going to introduce the most efficient way and that is by importing funk tools we import something called lru cache and what this is going to do it's a decorator that we will define over something like git settings and then we want to return an instance of that settings so this function here and this decorator will mean that it's only called one time so it actually caches the result from that which is completely fine for us that's really all we need and the use case of the actual settings themselves inside of any given view is actually handled just slightly different as well and that is we bring in a depends class in here and now what we do is add settings the result of this as an argument inside of our view much like we do with our request and this is of data type setting like that not initialized and of course that's going to be equal to this depends class and then it's depending on this function right here so this is now a view that's handling these settings and it works like all of the other classes you might use in your project anyway and so the idea that now is every time i need to use settings on any given view this is what i want to do and of course moving this into other modules is certainly encouraged so now if we look at debug here how do i actually handle this portion well in this case i actually wouldn't leave it as a configuration on main.pi i would literally only use debug where i need to and i can still call this git settings method here right so if i call it up here we should still get that value git settings then dot debug we should still be able to get that value from there in the first place oops we have a little area here this should be from funk tools but anyway so now we've got that false in there right so it seems like it's not actually handling it correctly right in the sense that hey i've got debug in here as one but this is saying false okay so if we go back into let's say our home view and this time i'll go ahead and print out settings and debug so let's go ahead and actually navigate to our home view here just gonna restart the server and open up this page okay so it's still saying false now why is that right so we've got the problem right here and this should be env file not filed and now it's doing it okay cool so two different ways on how you can actually still use those settings and this lru cache will just make it efficient that's it but overall what i would actually end up doing with this is saying settings equals to get settings and then settings dot debug like that pretty cool very simple but definitely something i'll use in the near future now we're going to go ahead and handle file uploads in fast api so it's going to be very similar to our post method here except it's going to take in a argument for the actual file that's being uploaded this is probably not that surprising but what i want to do is also return that file so i'm essentially echoing this file and i'm going to call this the image echo view and i'll just give the path of img img-echo and that's it okay so how do we actually implement this now first off let's focus on the input itself and so the input is going to be based off of a file class so let's go ahead and actually put in some parentheses around this and separate these out because we've got a lot of imports now and so we're going to bring in the file class itself as well as the upload file class and so what we want to do with our input the actual upload itself is going to be based off of this and we'll give it a upload file type and then it's going to be equal to the file and then using ellipsis we'll give it an arbitrary length of whatever that file would end up being and so this is now going to be our file but we have one problem and that is how functions end up working is well we could actually load this function but it's going to load asynchronously so we have to do async and then the function name and now we're going to want to get the actual bytes so we'd say bytes or actual file bytes is equal to await file.read okay so those are the actual file bytes that we'll end up having now using just simply bytes is really not that valuable to us so what we want to do is import io and turn it into a byte string so this time i'm going to give it a byte and string and this is going to just be io.bytes io and then grabbing that byte string that's coming through right here so now that we have a byte stream that creates a byte string we're going to go ahead and actually save this file somewhere at some point and then we'll return it so before we even save it what i want to do is set up a place to save it so we already have our base directory here and so i'm going to go ahead and currently use a uploaded dir and i'll give it based off of this base directory and we'll just say upload or upload it either way okay so now that i've got that i have a destination in mind so the destination is going to be that uploaded directory and then what else well what i could do is i could actually use the file name itself so whatever is being passed in with this file i can use file dot file name now there actually is a downside to doing this and that is what if i upload the same file with the same file name over and over and over again well to solve this we're going to use path lib and another package called uuid so import euid okay so this package itself now what i'm going to do is come in here and say fname equals to well this file name so pathlib dot path of that file name just like that and what i want here actually is the suffix so i'll use f ext equals to fname dot suffix this will give me you know jpeg or txt or whatever right and then the actual destination what i'll use is a string substitution variable here so uuid dot uuid 1 and then dash or rather not dash but just f ext now if you've never used uuid one it's a uuid with a actual timestamp as well which is really nice to add to the actual file name granted it's gonna be stored when it's actually created as well but it's also nice to have it actually in the file name so you can parse that a little bit easier so now that we have this destination here let's actually save this now you might already be familiar with open and the string of whatever the destination is going to be and then wb as out and then out dot right and then this would be the actual byte string itself and then we would read that and then this would actually save it to that destination so we could return back that destination now the thing about this is if you remember back to our template here we actually had an html response this time it's not a json response which is the default which is what this is and it's also not an html response but instead it is a file response and so we'll go ahead and implement that one and that's simply just response class is equal to that file response cool so i'm going to get rid of some of these print statements and comments there is still another thing now this is nice and all for local environment testing now that's actually the key here is i'm making this for just testing what our upload view would end up be like right it's not going to be our actual upload view it's not going to be our actual prediction view at all it's just a test so since it's just a test we need to add in one more thing into our environment variables and that is going to be echo active and we'll set this equal to a boolean type and we'll give it a default of false so echo active of course and here we could say something like this 1 for true zero for false and i'm going to go ahead and just say echo active like this and so that will infer to this same variable definitely check it out and try it out on your own if you don't believe me but now i'm going to go ahead and also copy in these settings here and bring it into this file and really what i want to check is if not settings.echo active then i want to raise what's called an http exception now these are pretty common and something that you will certainly use later because we definitely need it in another view so we'll do http exception as an import here and we're just going to bring this in and raise it in and give a detail being that invalid endpoint not really not found but just invalid and status code being 400. okay so now when i go into production i will have a lack of response so this file response just simply won't happen unless i change the environment variables so that's pretty cool now i could also add a header which is something we certainly will do but the idea here is we now have a way to actually upload files this also means that we need to test this so that's something we'll do in just a moment but just to recap you whenever you're actually uploading files you want to use an asynchronous view this is why we use uviacorn as well so if we look at uvicorn and app.main app and then reload uvicorn actually is set up and ready for asynchronous views and asynchronous web server gateway interface and that's also true for this uv acorn worker inside of g unicorn so both of those things together really allow for an asynchronous view like this to work on a web application which i think is really really cool but we're actually missing a couple things for this view so what i need to do also let's just open up a new terminal window here and activate our virtual environment i need to go ahead and do pip install dash r requirements.txt and of course i need to add in some new requirements those are related to uploading and we would see this error happen if we actually ran a test yet but i haven't ran that test yet so i'll just do ao files like that and then python dash multi part like that those are two that we'll definitely need and then we'll also need pillow eventually which is the python image library to actually verify proper images being uploaded so go ahead and add those and save it if you haven't done that already okay cool um so now we have this directory for uploads it's time to actually test our uploads themselves now let's go ahead and create an automated test for this endpoint and i actually added in a couple images in here just to test the file uploading purposes here now of course this isn't an actual image check yet there is no image validation but there will be for now i'm going to go ahead and jump into test endpoints and i'll copy this test post home but instead of course changing it to our image echo view so image echo with that trailing slash right just like that okay so to actually post a file it's simple we say files equals to some file argument here and then we're going to open up a path and we want to read bytes that's it i'm going to get rid of the notes there now what is this path going to end up being that's really the question here we'll do that in a second and i'm going to call this test echo upload okay so the path itself is going to be based off of these images so what i want to do is inside of my imports i'm going to go ahead and import the base directory from main.pi of course it's that right there i could also declare that but just importing the base directory makes things a little bit more simple on here and so the path itself is going to be equal to you know one of these files now there's many different ways on how we can go about doing this so if i say images this of course is the image directory and what i can do is i can actually put all of this in parentheses and do a glob and say star that will literally get every file that's in here and then if i put all of that around a list and then save up to you know one or something then it will allow me to use that specific path but what would be better is if i actually tested all of the items that were in here just to make sure that they're all working it's certainly going to take a little bit longer but it is something that i think is well worth doing so instead of doing that i'll go ahead and say image saved path and we'll give it the base directory right here and then we'll go ahead and iterate through for path in image savedpath.glob and everything then we're going to go ahead and push this through get rid of this so the other thing is you might actually want to only test png images that would be a way to do it or txt images yet again another way to do it and so in this case it's just simply every file that's in there because the other thing is we could always put in a text file in here to verify later and so the response type should not be application json but let's go ahead and give this test a shot so we'll go ahead and do pi test and dash s so we can see any printout that we might need and again image save path is not defined now we have the error no such file or directory so what we see is if we investigate this this path right here is what we need to verify and my suspicion is that we actually stored the uploaded file in the wrong spot which is actually where this error is happening and so we can actually go into that function and take a look right so let's go ahead and jump into main.pi scroll down and this is actually where the problem is and so my suspicion is upload der right so whatever that directory is i need to check it out and so if i scroll up a bit what i see is my upload directory says uploaded and so if i actually change this to uploads like this directory right here then it should actually work and so i'm going to go ahead and also do uploaddir dot make dir or mkdir exist okay being true now the actual reason for this is also to delete this after we run our tests so inside of test endpoints i'm also going to do import right above fast api we're going to import sh util and also import time for two reasons but after these tests run i'm going to do shuutil.remove tree and that's going to be that upload directory okay so i'm not going to do that yet and i'll also do time.sleep so we can make sure that this is all happening i'll get back to that in just a moment but now that we fixed that upload directory let's go ahead and do a quick test and now we get another error saying assert application json is not in image dot slash png so in other words this error is failing no surprise here because it is an actual file so instead of inserting these what we can do is take a look at the actual response headers to see what's actually coming back from this request so we run this and hit enter and sure enough it does pass make sure you use that dash s otherwise you won't see those print statements necessarily and so here is the headers that are coming back we get content type content length which means that it's actually content like it's a file that's coming back from it and it's an image and a png image at that so this is cool and it actually allows us to do a couple things number one i can now test path dot suffix so if i actually run this down what i should see is dot png right so what i want to do then is check that if this path is in this content type now this is not always going to work right so not every file will do this but the files i'm testing it will and also when i go into images all images should actually validate in the same way what i mean that is i'll just do image ext or let's do f ext to keep some consistency here i'll do path.suffix and then i'll put this into a string just to verify it is a string and then we'll do replace the period with just an empty string and now what i want to do is just check we'll assert that that actual extension is in response headers content dash type okay so that's based off that path so it certainly should be right so i'm going to get rid of these now and we'll run it again and this time i've got oops i should have deleted f text i should just delete those print statements there we go and so now what we should see is valid all the way through now this is okay for what we currently have but it's not great uh in the sense that it's not actually testing an image right so it's not verifying any image in here it's also not verifying that it's sending an image back or the exact same file back in other words right so right now this should be echoing that same file back but we can certainly test more things related to that if we really wanted to get nuts i don't want to get nuts yet instead what i want to do is implement this right here so we're going to go ahead and implement time sleep and sh util remove tree of this upload directory that we still need to bring in now the reason i'm doing this right here is because this test echo upload is a temporary thing right we don't actually want to keep this upload directory forever we only want to keep it when we are doing this image echo in fact when i go into production when it's actually in production we won't save those images at all on our local machine a big part of that is because of the docker file i'll talk about that again when we get there but for now i can actually see these uploads so all of them are here from all of my tests but now let's go ahead and run it now with some sleep in there so it's going to take a few seconds it's going to show me the new ones and then that whole thing is gone right and so really when i'm running my tests i don't want that at all i just wanted to run those tests put it into a temporary directory and go now the reason i'm not actually using a proper python temporary directory from tim file is because if i ever do want to see what's being tested then i could just you know either sleep it or comment it out again and see what the actual values are uh that's a big part of the reason i'm doing that so this make directors file or folder actually probably should exist in here so it shouldn't always make this directory in here but rather do it on the view itself during the testing phase or really during the echo active phase and so yet again there we go now it's gone and it literally will not come back up until we run that end point again the final test would be actually changing this to zero the active being zero and this time it should give me that status code now that's not something i'm going to implement at this point we will actually test the live echo version later but for now we're going to just test it just like that so just our local environment tests they're all set up ready to go now it's time to validate the actual file upload itself as an image file now what we need to do is implement the python image library to validate our upload being a proper image so to do this we're going to go ahead and go to requirements.txt add in pillow make sure that's in there if you don't already have it in there and then in back into main we're going to go ahead and import we'll go from pill import image the image class itself and so now into our echo what i want to do is i want to turn this byte string here into an image itself so we'll go ahead and say image or image equals to image.open let's actually call this img and we're going to try and open up that byte string so this is actually how you'll do that oops we need a trailing parenthesis there but this is how you'll do that right you'll actually attempt to open an image there and then instead of actually using the open bytes and all that right here we can just do image.save and then change the path to whatever that destination is that's kind of why i actually used open and all that in the first place is because i knew i was going to end up doing this okay so now we can actually manipulate this image in here as well right so that's another thing the python image library gives me a lot of options to manipulate an image not just validate that it is an image now it's not always going to work perfectly right so there definitely is challenges that could arise using the python image library only so you could also consider using opencv also known as cv2 so opencv would be another option here i'm just not going to cover that right now but anyways so now that we've got this image we want to test all of this out and we should have no issues with the tests right now so if i do pi test s it should work just fine but that doesn't really tell me anything right it's kind of false confidence in fact in my image folder what i should be doing is creating something else so not an image dot txt and say hello world right so now that we've got a non-image in there and we try to upload this now i get this unidentified image error right so in other words if this can't open the byte string whatever it is i'm just going to assume that it's not a valid image even if on your local machine it's supposedly a valid image i'm going to assume that it's not and they'll just have to reform it now i'm actually using this as a micro service so it doesn't really matter to me if i did that right so um like as an end user you might be like hey what the heck that isn't a real image why is it saying it's not but since it's a micro service i can worry about the actual image part later like actually in the other service not necessarily this one this one's going to be a little bit more strict is kind of what i'm getting at so with this i'm going to go ahead and use this exception again and i'll go ahead and put it into a try block and do accept and then this time this will catch every possible error that happens with this now what i'll do is just say invalid image that's it right and so this one now would give me an invalid image on one of these items right so inside of my actual test it's it's doing it correctly it's giving me that error response so this means that my test itself needs to be improved a bit right and so that is i'm gonna have to you know maybe update how my actual iterations are coming through or i could change this ordering right here and really look for what this is and i could say just let's go ahead and say valid image extensions equals to png jpeg and so on i'm not going to end up doing this don't worry in the long run this is not how i'm going to be testing it but i'm going to go ahead and do this and say that if the extension is in here so if f that x t in here and that is essentially what we might end up doing and this would actually solve that test right it does solve it but it's not really solving it right it's not actually validating that that image is incorrect or correct so you might have some sense as to what i could do here maybe not but it actually has something to do with pillow again or the python image library and that is to actually import it so we'll go ahead and do from the python image library we're going to import the image itself and this time i'll go ahead and say try the img equals to image.open and path otherwise we'll go ahead and accept and say img is none this is a much better test we would say basically if img is not none then we'll run these otherwise we'll go ahead and assert that the status code is 400. right because this extension thing is not reliable and what's actually better with this test now is it's actually testing how the view itself is functioning at least in terms of the image itself it's actually not testing the response at all that is something we do want to test as well so let's go ahead and run this and now i got oh f text is not defined let's get rid of this right here i'm not going to test that extension thing anymore and so now we've got the valid responses of the valid tests all together okay so now what i want to do though is i actually want to test the image itself the response image so what i'm going to do here is i don't need this try block anymore i shouldn't have to have this try block at this point and i have image being none so if this path is not an image we aren't going to validate this so really i want to re do these have it being if it is done then like that otherwise we'll go ahead and say that okay so just reversing it a little bit because now we can assume that this is the image and again i'll go ahead and say the r stream or the response byte stream is going to be response.content and yet again we're going to import io and so we'll do io.bytes io the response content and yet again this is now seeing if this is a valid image response right so let's go ahead and make sure that this says this is returning a valid image or in theory it's returning a valid image okay so now that we've got this stream here what can we do with it well i can do echo img equals to image.open of this stream now when this run runs an exception that's actually something i want to see that would be a malformed image as a response from my view so it's a proper actual failure in my tests right and so now we've got that the next one is we actually have the original image right here which is that right so we can actually compare two images from python image library with something called image chops and so with image chops what we can do is we can run the difference of the echo image and the original image which was img this order does not matter and then we can do something called dot get b box and what we want is this to be none right so difference equaling to that and then we're just going to go ahead and assert that the difference is none that it's literally the exact same image right and let's give this a shot and there we go cool so we could take it another step further and actually save the image itself and then test the save as well but i don't think that's necessary i think it's fine just as is we don't need to overly test what this response would be because we're most likely not going to ever give that response back in a production environment as we've already set up but it's still really nice to see that we can test all these things out and just ensure that hey we now have our functioning echo itself and we are fully able to test it so we can have the full confidence that this endpoint or at least parts of what this endpoint does like these things that it will actually allow me to then run a machine learning algorithm on this image now the reason we are using docker and docker files is because of this line right here line 12 tesseract dash ocr so tesseract is the machine learning model that will allow us to extract recipes the actual data from a recipe like what's in these images right here it's going to extract that text and turn it into something we can actually use so it's really cool that tesseract does this and we're going to be using that now so we can always test this on digitalocean we can go into our console and use the the tesseract app itself there but it's a little tedious and really not that practical for the vast majority of us so what you're going to want to do now is actually download tesseract ocr on your local machine so check out the documentation for it the user manual i think is pretty good for the downloading but if you have any issues with it please let me know in the comments now if you're a mac user check out homebrew homebrew is a really easy way to install pretty much anything and of course if you're on linux it's just apt-get install you probably already understood that in your terminal obviously not every linux distribution uses appgit but for the vast majority of you that's probably what you'll have now if you are on mac then just do brew install and tesseract like that and i believe it should work just fine on windows it's just a little bit more steps to make it happen and then once you have it installed you should be able to do dash dash version and get something like this so that's what we're going to end up using with tesseract now the next thing is going into requirements.txt and having pi tesseract so you want to make sure that those are definitely in there like they're absolutely working and so what we're actually going to end up doing in a digital ocean in the console we will actually use tesseract in here just to make sure that we are all on the same page and so what we're going to do now is we're going to jump into our app itself i'm going to make a new file in here called ocr.pi this is really just a python script that we're going to use just to interact with pi tesseract i'm going to show you how easy it is so the first thing i want to do is import path lib next i'm going to go ahead and import pi tesseract and then we'll go ahead and do from pill we're going to import image so i don't want this dependent on fast api at all i just want it completely with these three packages that's it so you could use this anywhere else that's kind of the point so to do this what we want to do is set up our base directory and that of course is going to be this file so pathlib dot path of this file and then i'll go ahead and do parent and now i want to grab the image directory so img dur equals to of course that base directory and images now i'm just going to go ahead and grab one of the images so i'll just say image one equals to that image directory and then we're going to go ahead and grab the ingredients one pm png okay so yes we could loop through it like we did with the test but that's not really the point of this the point of this is to show you how easy it is to use pi tesseract first off we need to declare the image let's actually call this image path instead so we're going to declare the image with image open or dot open rather from that path right so python image library path that's it then we're going to go ahead and do pi tesseract dot image to string so this actual method makes a lot of sense to me is it's hey what's the image turn it into a string as in extract all of the things in there and these are really just predictions right so we can just say preds equals that and then i can print out what those predictions are and that's it so if we want to test this locally assuming that we have it installed i can run python app and ocr dot pi hit enter and what do you know how cool is that it's now giving me the actual text from that image right and it's it's pretty much identical i don't think i see anything missing here now there are ways to improve the extraction that's not something i'm going to cover in this one at all because this is really just about how easy it is to run a proper prediction now if you are familiar with deep learning or machine learning it's actually not a whole lot different than this a lot of times it's model.predict and then using like maybe an image or an image array and then it's going to be some sort of prediction as well so using actually machine learning in the same fashion well we are using machine learning because pi test right but using your own custom model would be very similar to this just slightly different but this of course is not the predictions that we want back instead what we want back is it like a list of these things so what i'm going to do is say predictions like an actual python list and that's going to be equal to well let's say x for x in these predictions and we're going to split it by a new line because i noticed when i printed it out it actually printed out new lines in here so it's pretty safe to say that i can split it up by those new lines and now i can actually go ahead and print this out and run it again and what we see is all of this stuff and i think what's kind of cool is we can actually think of these spaces as new lines here as well we can consider them as that so they're like empty list right which is i think another nice thing i'm actually going to leave it just like this so that when i bring this into production i can consider it a little bit more like that so now you're probably wondering well how do i implement this in production right or how can i test this in production well of course we can grab this whole thing and do it all over again right so let's go back into our digitalocean app looks like deployment worked so i'm going to go ahead and jump into the console let's just make sure that that's running correctly so inside of the console now this is where i could test it so if you had issues installing pi tesseract on your local machine here's where you install it all right check it out so we'll go ahead and do echo so or actually let's see if nano is installed nano is not installed so we're going to go into cd app here and what i want to do is i want to actually use python in here and this time i'm instead of having the file in there i just want to see that the parent directory is correct that it's the same directory that we're at and there it is should be good okay locally i'll leave it in as file again we're just doing a quick little test in production to see this thing work predictions coming through whoa what do you know it's exactly what we needed that i think is pretty cool now this obviously means that there's not a huge step that i need to take to actually implement this in production implement it for real on a real view all i need to do is import pi tesseract and then i need to essentially copy this i'm going to actually replace my original home view and i'll just call this my prediction view right and it's literally going to be that home page here and what is it that i want to respond this time i do not want the file to be responded at all instead i have all of these things and hey what do you know i have that actual image again so yet again i can you know delete all of these things just going into ocr i could just copy you know these two lines that i wrote out okay and so this is going to be a string so we shouldn't be too concerned about the data type in here which means that it should be able to return results being whatever the predictions are right that actual list and i could say the original like the actual original string we could also bring that back in as well because perhaps i want to do something with that original string too like i don't want to decide that for my other service just yet i want to get the most information i can from this microservice when it makes sense to me that's pretty cool like we didn't actually change a whole lot and we could feel pretty good about how these things are coming through now of course we can actually test that and we can test it on this same sort of test let's get rid of some of these spaces here and i'll go ahead and copy this bring it up and now we're going to go ahead and test the prediction upload okay so same response code should happen if it's not an image right and also same response here should happen if it's not an image this time of course it's not echoing back an image this time it's going to give me actual data right so really i'm going to just go ahead and say data equals to response json right so whatever that data is i want to just assert that the length of data.keys is probably two right something we've seen before because i won't know exactly what the data is i probably shouldn't know exactly what that data is in there but let's go ahead and print out the data just so we can see it also being used right so pi test dash s and here we go so i get an error here we got a decode error right i know why we use the wrong endpoint okay corrected endpoint let's run it again this time we get a different response we'll fix that in a second but here we go so here are our results and our results that's pretty awesome right it's giving us exactly what we were looking for now what's strange is we've got a response of 422 in here as well right so what's happening with this it's this one right here so it made up high this time the echo active should be gone so that's something we definitely don't need so invalid image we should be here i don't need to make the directory either let's get rid of that because i also don't know if i deleted it but i definitely don't need it in this okay so let's give it another shot yep still getting an error okay so let's just print out in our test here i'm going to go ahead and print out what the response text is see what the problem is nope getting text and i kept thinking that the error was in this right here but i forgot this right here this is actually doesn't have a file in it so we're posting something without a file and thus we're getting this 422 error so this time i should actually put that in as a test invalid you know file upload error something like that and let's see if the application json is what's coming back the response itself should not say json it should not say hello world but if we test it out all of those things are coming through the response itself is not in here like this at all so go ahead and get rid of that okay we can also get rid of the response text here because that's all valid we can get rid of that data itself as well and let's run this test again and sure enough everything is looking pretty solid okay so yet again another thing that i should probably test is this error with the incorrect files like actually getting that exception now right so in other words i want to make sure that this response path um is not just 400 but actually uploading invalid files and just verifying that all kinds of file types are invalid not just this one txt file and maybe even a txt file or an actual txt file with the proper image extension and stuff like that but again these are like going down the rabbit hole of too much testing for this video but there we go we now have python ocr on here now the other thing that we still need to implement that i still want to implement and that is adding one level of security that this view does not have and that is adding a token header to just really round out this microservice the actual deep learning part of it or the machine learning part of it for predictions is ready to go it's done now we're going to go ahead and add in some authorization headers to protect our prediction view now this is not the same thing as authentication as in we are not going to be setting up users and all this permission instead we're going to just do one-off tokens that are going to be stored in our environment variables and then we'll verify those in here so the first thing is i actually want to create a token so let's go and do that with inside of python we're going to import the package called secrets this is built into python and i can build a token with simply secrets and dot token url safe and then some number like say like 32. i'm actually going to do this twice so this very first one we're going to go ahead and put it in here as simply app underscore off underscore token and we'll set it in just like that now the thing that we want to do with the app token is we also want to put this into production so that's what the other one is going to be and i'm actually going to keep it local too so app token and prod so we now have two new environment variables both of which i'm going to put into my main settings configuration so app off token now this one is going to be a string right and then app off token prod is going to simply be a string as well but i'll set it equal to none so the app off token is really the main thing that we'll definitely need in here when we use this application and so we want to save emv and we might have to refresh our our project here and reload it like that now if i were to change this to let's say two and i left that setting as being required i'll get this you know validation error so certainly if i push this into production now that token being gone in production would bring a 500 error an actual server error so what that means is then i'm going to go ahead and bring my app off tokenprod into production okay so the production token should have no effect on the server itself which we could try out and it does okay so i'm going to go ahead and bring this one in let's copy this token and go back into digitalocean go into settings you can put this on the component level environment variables or the app level it actually does not matter i'm going to keep it on the component level in environment variables then we'll paste this one in i'll go ahead and encrypt it and then we'll do app underscore off and token we'll go ahead and save that now the reason i have a production token in my local environment will have to do with testing later for now we're going to go ahead and set up the app auth token for local testing so that actual token is going to need to be inside of our prediction view here so instead of actually making it in our prediction view we're going to do it inside of another method and we'll just call this verify and off this is not going to be a url route at all instead it's going to take in a couple arguments the first one is the authorization argument i'll come back to that the next one is simply settings and of course it's going to be exactly what this is right here okay so now what i could do is basically say if settings dot debug then i could just return right so if i needed to actually use the settings and trigger that in any way i could do return so the other thing is perhaps in my settings i can add one more aspect of like you know skip off right skip off and being a boolean and setting that equal to false okay so skip off in here and we'll set that to one and so now this kind of comes double protected and we can do debug and settings dot skip off and we can use lowercase here so in that case off is verified it's just going to return back but now we actually need to do the header the authorization header so to do this we need to import the header class from fast api and we're going to set it up very similar to request or settings it's going to be of the type header and really it's just going to be equal to header being none okay so this is going to be the same thing on our view as well not surprising and so now we have this authorization header now what is an authorization header well if we look at headers in general what you'll end up seeing is something like this authorization and then bearer and then a token right it's not always bearer but that's essentially what the header is going to look like and when we convert that to a key value pair inside of the actual request headers it's going to be something like this where it's authorization and then the string of bear token and so what this argument does for this view is it unpacks that it just gives us the value that's right here for whatever that authorization header is so that means is that i can actually parse this right here and that's actually what we want to do we're going to go ahead and say label and token is equal to whatever that header is and then dot split now i should actually verify if the authorization header is none then i should actually raise a hey this is unauthenticated so in other words they're not you know they never actually put in their proper authorization header not authenticated but authorized okay so now that we've got that the next thing is just to test the token itself so if the token is not equal to the actual app authentication token right so this right here not the prod one but simply settings.app authentication token then again we'll go ahead and raise uh this time it's actually a 401 error okay and so all we need to do then in any view now is call this with the authorization and the settings as the arguments that we need okay simple enough so now going into our prediction view actually if i run the tests now what we'll see here is pi test and then s we should get an error right but we don't our tokens are actually not coming through so we've got skip off let's actually print out what our the dictionary of our settings are take a look at that and so here we go and we have skip authentication being true okay that's because it sets one let's change it back to zero and try it again now we get the 401 error right so we actually get the authorization error cool so that block's working and so is this block actually this is the block that we're seeing not the token one so how then in our tests do we add the headers well it's going to be in our test prediction upload view right so the actual endpoint that we just set up i did not set this up on our test endpoint at all and i don't think it's necessary to set it up on our test endpoint but here is where we're going to need to put our headers so now what we want to do is test this a couple ways but first i'm just going to test it properly like the actual token and there's going to be headers equals to well i have to get the settings again so we're going to import git settings okay so i need my environment variables at least my local ones and it's going to be settings equals to get settings and now we're going to go ahead and put in our authorization header so authorization and it's called a bearer but if i said jot here and use a different token it should still work because it's going to split it up so app off token okay so this part doesn't work it doesn't matter in our case now you totally could add that as another feature that would make it matter it's really just the token that matters and so now if i run this again we should be good to go and sure enough we are so the other thing is i could try this same thing out as another view without the token itself and i just want to assert that the response now is going to be simply 401. no matter what i'll cross all the board so now we have no token so let's just get rid of those headers uh prediction upload missing header okay and we run it again got no errors i'm gonna get rid of those print statements and those might be in here there we go let's get rid of that cool so very basic way to get our secrets in to production and it's an incredibly useful thing because now if i ever need to change the secrets to this endpoint all i need to do is jump in at least the token itself is jump into these environment variables and just change what that token is and this is meant for my one application to communicate with another application and really it's using open source technology anyway so it's not like um having this exposed is really leaving me vulnerable to anything other than server costs right like maybe my server gets to be a lot more expensive that's kind of kind of the gist of it but now that we've got that we need to actually push this into production and then test the actual production version uh all of that i'm gonna just do off the video and then we'll have it actually running into production with this latest version so do that now now my application is fully in production and if i go into my settings i just need to verify that i have environment variables and it's related to the app off token so since i have those things it's time to actually test this the actual production application itself so i'm gonna go ahead and copy my current tests and i'm gonna make a new one and we'll call this test production and we'll paste this in here now there's a lot of things that i do not want to test including the echo statement there i could test the home page actually this would be generally a good idea to make sure that i am fully testing the application and making sure that it's live so you can run these tests in all sorts of places so the main way to test this is of course grabbing the actual endpoint itself which is this and i'm going to declare that right up here and we'll go ahead and call this just simply endpoint okay and so the endpoint itself is what we're going to be using we're no longer going to be using the client itself we no longer need that in fact the only thing we should probably need is git settings potentially these upload directories as well but something i will need for sure is requests so i've been mentioning that python request is essentially how we're doing this and that's what we need to change all these two so just simply requests and the endpoint right and so down here requests and the endpoint right and i want to actually test a lot of the same sort of things in production but the thing is i might not always want to run these production tests but i think it's a good idea to get in the habit of doing so so again requests post and here production yet again requests posts and here now one of the key things about this test that's a little different is adding a different token which was that prod token that's right there so that prod token is also what i would end up using in another application right that's fairly important actually so now let's go ahead and run pi test and of course it's going to take quite a bit of time on production now the reason it's going to take a while is well a couple fold number one however many files that are in here and however big they are are going to take some time to upload number two the speed of tesseract to actually do a prediction on any given server is going to vary so depending on what your service is at like what level it's at it's certainly going to vary on how quickly it responds right so like right now i'm on a basic plan for five dollars a month if i up that up to have you know more ram or more cpus then you know perhaps it would be a little bit faster but that's it i now have actual production tests so i can add this and then do git commit and production ready and then we'll go ahead and push this into full on git production itself in this one we are going to deploy our fast api microservice for our tri general project now we're going to be doing this using the one click deploy method on digitalocean which makes things really a lot easier and a lot faster especially for a microservice like this where we built everything out now make sure that you already have a digitalocean account of course you can find this on do.co cfe sh it is also on the github for this which is the fast api microservice github if you scroll down to the bottom you can click there as well we will be deploying here in a moment so make sure that you actually log in to your account and so what i'm going to do now is actually just hit this button deploy to digitalocean so this is going to open up a few options that we need to set up to make this work and the first one is creating our app auth token right so i certainly want to change this to something different something more secure and so to do this we're going to jump into any version of python 3. we're going to import secrets and just do secrets dot token underscore url safe in some number and we could do this multiple times okay so let's go ahead and grab this first one here and we will paste this in and encrypt it encrypted it means that we won't be able to see this password ever again so if you do intend to use this one make sure you make note of it or we can reference it later or we could always reset it later which i'll show you as well so let's go ahead and scroll down a little bit hit next and what we want to do now is just verify our name fast api microservice for django and then you can select what region you want to use here we'll go ahead and hit next and then you're going to want to set up the actual size of this image like how your cpu is set up with the various things related to that so we're going to use 512 megabytes of ram and one cpu that's probably enough for this one and i'll go ahead and launch this app now it's going to take some time to actually build this for the first time the reason for this is because it actually uses docker as its underlying technology granted we don't actually have to spend a lot of time with docker if we want to learn how to do all of the code here but that's what's going to happen every time you do a deployment it's going to build out a docker container with a bunch of configuration that's actually required for this particular microservice it's not required for app platform but this one in particular okay so the next thing is if we wanted to change our actual environment variables we would come into settings find the component in this case it's just web and then actually run off of this so inside of our environment variables you'd come in here and just change that now do know that if you do change it make sure you encrypt it again but if you save this it actually will rebuild the service right then and there so cancel the old build the current build and then it will build a new one which will take some more time i'm going to let this finish building and then we'll come right back while it does finish building what i want to show you is the code so if you go back into the repo itself what you will see and how you'll end up using this is by checking out this endpoint here so we've got this endpoint now we can actually go from this endpoint and use python requests to upload a file really it's just this right here so we're using that endpoint and then we open up some sort of file in the bytes mode we are reading bytes and then we pass in the headers for authorization in this case it has a jot header um that actually doesn't matter this label right here does not matter at this time maybe it'll change later check the readme for the repo if it does but then we actually set our token here whatever that token might be and that token of course was that environment variable that we set right and so if we look in here it's this token now the reason that the tests has a production version is because we need two different tokens one for this local testing and then one for your actual deployed project so if you end up going that direction but that's really the main thing to interact with this as you notice it's incredibly simple to do and now of course in my try django project i'm going to be deploying this again specifically for django so if you end up using the django version of this by all means you can watch that one as well now i want to actually finish this being deployed so we can actually see what that endpoint's going to be now i realize there's one other thing i want to show you with this build and it's based off of something very simple inside of the application code itself so if we go back into the repo and go into do we see this deployment template in here so this is actually showing digitalocean what to deploy from and that's these things right here and more specifically the docker file so it knows that it's going to use the dockerfile in here which i think is actually really nice now it's not necessarily going to get everything in here but in the future it might and this also might update and change so be sure to check out the repo itself if something like that were to change but the other cool thing about this if you've watched the entire series is you'll know that this is actually how you can make this really easy to redeploy across well any of your organizations or any of your applications so in other words if you wanted to deploy this on a new project you can do that like let's say for a client or just another digitalocean account you just click this and boom there you're going to go or you could always update like actually fork this code update the digitalocean deploy template to whatever you need so that's another aspect of this now in the long run you know when it comes to microservices like this or any sort of code that you want to use a lot of forking it is a good idea to make sure that you at least have a copy of it in the future or cloning it locally that's something i definitely recommend for a service that's not going to be part of your core service especially okay so at this point the build did complete and it's just finishing the deployment of it and the main thing is i just want to verify that the endpoint is actually working i click on it this is what i expect to see at this point maybe in the future it's going to change but this is a microservice api so to actually use this we would have to come in to using python requests so inside of python requests like the code let's go back in here and zoom in a little bit and then test production we would change this to our new one this right here and then we would run we could run these tests as well with using pi test if you're not already familiar with that and depending on when you're watching this so thanks for watching this little demo on this one-click deployment i realized like some of the time of it took a while just to get it up and going but really it's pretty simple as to as how you need to actually set it up so um hopefully you guys make it happen and this actually adds a lot of value to any given application you end up using so good luck hey thanks so much for watching hopefully you got a lot out of this one now of course if you haven't seen my trijiango 3.2 series yet definitely check that out that is much more comprehensive than what we do here especially as it relates to handling and managing users handling and managing like forms and input data there's a lot to it but even if you want to know more about fast api and how you might do all of those things let me know in the comments but the general idea is we now have a pretty solid foundation to actually build on top of fast api if we want to make it more than a micro service or we have a solid foundation to build many other micro services which i think is great too so thanks again for watching and i'll see you next time [Music]
Info
Channel: CodingEntrepreneurs
Views: 7,913
Rating: 4.9813952 out of 5
Keywords: install django with pip, virtualenv, web application development, installing django on mac, pip, django, beginners tutorial, install python, python3.8, python django, web frameworks, windows python, virtual environments, beginner python, python tutorial, djangocfe2021, digitalocean, production, python, web apps, trydjango2021, fastapi, flask, rest api, rest, microservices, django microservice, pytesseract, tesseract ocr
Id: JxH7cdDCFwE
Channel Id: undefined
Length: 147min 54sec (8874 seconds)
Published: Sat Sep 04 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.