OK, well, let's get started. You saw the intro to a documentary made in 1972 about ARPANET, which was quite easily and quite arguably the first internet. And as I'm sure many of you know, these communication channels were built in the technologies of passing information around in the form of packets with what we now think of as an IP address. We're formed in the late 60s, and then the first networks were built in 1970 in California. And then they built out from there. What happens after that? Today's lecture is on web development with Python. And we've seen bits and pieces of our interaction with web pages already. We now know through URLlib and URLlib2 how we can get data and how we can post data to other servers. Effectively now, we're going to be talking about building a framework for you to be able to present your own data to collaborators, to build a communal site, perhaps, for you to interact with your collaborators, and so on. Today, just like almost all of our lectures, we're going to be fairly agnostic about the specific application and just give you the tools that you'll need to be able to build whatever it is that you're going to want at meeting. I suspect that many of you for your final project will wind up using a lot of what we do today as part of that project on building just a web framework for web framework sake and building something that you can interact with just to do that is obviously not all that interesting. What we're hoping you do is you pull bits and pizzas from the rest of the course, and perhaps you wrap it around in some sort of interactive website that you and your collaborators can use for doing science. So we'll talk about the web paradigm and then essentially using Python within that paradigm. We'll introduce basic Python servers, and then we'll talk about frameworks and we'll use Django, which is sort of Python's answer to Ruby on Rails. And then I'll mention at the end a bit about the Google App Engine. So I'm sure many of you know that the internet was not made for Reddit and passing around silly email messages amongst friends. This is a terrible reference the late Senator Stevens from Alaska who said that the internet was a series of tubes. I just had to do another meme because it's highly relevant. I found this on the web the other day. Code of the in-trade DB homework early, shorted Santorum and made a lot. If you knew that he was gonna lose Puerto Rico yesterday or whatever last week you'd be making a lot of money. So what the internet was really for was for scientists to communicate rapidly and to pass information around. The ARPANET as we saw at the beginning of that documentary was 50 kilobits a second in 1970. ARPA stands or used to stand for Advanced Research Projects Agency. It now stands for Address and Routing Parameter Area. And there's obviously outcroppings of that agency but it was effectively funded in large part by the US government. The first browser model came around in about 1990 and that was the so-called World Wide Web, all one word. That was before we're shortened to www. And that quickly morphed into something called Next. And then in 1992 is when NCSA Mosaic came around and then the founder of that started Netscape and then other things happened with the internet after that. So we've seen pieces of this already when we talked about XML RPC servers but effectively when we are interacting within a browser, what we're doing is we are asking something of the server. This is what we call the request. The server does magic and then it sends data back and then your browser will wind up rendering that magic. There are obviously a series of protocols of what it is you're actually passing around. We're not talking about the TCP IP layer, of course. We've talked a little bit about routing. We're more or less assuming that whatever layers we're using to pass our packets around are essentially able to deal with the concatenation of those packets and present those essentially at the text-based level to the server. And what we're not focusing on is the web browser stuff for now. We're gonna be talking about the thing on the right-hand side that's the server. So here is essentially a get request and it's saying, give me back index HTML and I know how to render things in HTTP 1.1 format. And the response initially says, okay, I know how to send you that kind of stuff. This is a 200 response and then they sort of get talking in this back-and-forth dance. The browser stuff, of course, is very interesting and making things look good within a browser is obviously important if you're gonna be serving out HTML, typically what will, and this is a reference for you to go check out the Google Code University, they have a lot of nice and very deep pages in not just HTML, but a number of other things including Python, hopefully you know Python well enough to be able to zip through that part of the site. But when we talk about browser stuff, and we've seen this a couple of times so that's why I'm sort of going over this pretty quickly. HTML is the structure, it's the language that is the markup of the information content that you'd like to provide to the end user. Generally, we like to break up structure, content, presentation, and behavior and the sort of nominal ways to do that now is with HTML5, something called cascading style sheets or CSS, and that's the thing that is used to kind of wrap around our HTML and essentially present that same content and structure in different ways. So the same structure and content can look very, very different depending on what kind of style sheets you have. You can change fonts, you can change backgrounds, you can change your interaction as you move your mouse around. The dynamic stuff generally comes with JavaScript and so you build little codes inside of your HTML and you send that and then the client within the browser knows how to do sort of dynamic stuff. For a while, there was a sort of sense of building dynamic HTML or DHTML, that sort of gone away. Now we're really kind of in this paradigm. Where HTML5 sits in all this, I guess at some level it's allowing us to do all these three things again in the same way but people are still writing JavaScript, people still writing cascading style sheets and they're still writing HTML. Okay, so what's Python's role in all this? Python is used to generate the HTML content and if it has to pass style sheets around, it'll do that as well. In general though, when you think about sort of making pretty pages, what you typically will wind up doing is create a cascading style sheet by in your own free time, you maybe even hire a web designer to do that for you. That's a huge ecology of people and ideas behind it. And same thing with JavaScript. What you'd like to be able to do is serve the minimal amount of content in the form of HTML that gets the job done for whatever it is that you want to do. And finally, we use Python to serve all this stuff to the end user. Okay, so let's go back a few lectures to the XML RPC server. XML RPC was essentially a different set of handshaking, a different set of requests and responses. Effectively, we're gonna be able to do the same thing. And I should mention for those that haven't already, if you go to B space under weekly lectures, there's a tar ball called week nine underscore web, I think that TGZ, please download that and you should be able to get some of this code if not all of this code. I'm not sure I have this one already. I think it may be called hello.py. But we can try to run this. So Python has a built-in module called base HTTP server. And you can guess what that does, that creates a base web server for you. And what you do is you create a class that has as its base this object called base HTTP server request handler. And so we can call this anything we want. This is effectively the functionality of your application will be put inside of my response. You overwrite something called do underscore get. It is a method of all of these base HTTP request handlers. And it is past something, only one argument and you say s.wfile.write, hello. So now we're essentially just creating a hello world program.  We establish where this server is going to live. It'll be on local host. You could say <IP_ADDRESS> for your, that's essentially an IP address for the lookup of local host. And we're gonna put it on port 8082. Typically your web server lives on port 80. And so if you don't tell a web browser which port to go to, it will naturally look at 80. I believe it will then, if it doesn't find anything there, maybe it goes to 81 or 88. But typically when you're playing around and you're doing development stuff, you wanna put your web servers at a high level port. Many of you already have web servers installed on your computers in the form, I think most modern distributions now come with Apache. Many of you may even have that. So if you've got some personal blog page that you use to take notes in classes, you may have started it up. If you're on a Mac, you can basically fire up your web server from within the preferences. And you can have essentially the full functionality of all of Apache. And that's kept mildly up to date for you. But this is the Pythonic way to do that. And then just like with the XMLRPC server, we have to serve forever. So essentially we're entering a loop and we're waiting for requests to come in. So W file is the output file stream that you wind up writing to. And one of the important things about what we have within Python, at least in the form of this base HTTP server, is that it is not threaded. So if I have multiple requests all coming in at exactly the same moment, this server is effectively gonna wind up queuing them up and blocking on all of them until the next one comes in. Modern web servers, of course, are ones that can deal with multiple requests simultaneously, can fork them off if they need to do lots of jobs, the different nodes inside of a cluster, maybe can even do this stuff in the cloud. But for now, this is just the basic functionality. So we can try to fire that up. If you're interested and see how that responds. Okay, whoops, that's weird. What are you doing? I'll just look at what they're. Oh, did I tar that up and I didn't see it? That's really strange. Huh, I must have moved something around. So you should be doing the same thing, I guess. Ah, without all the mistakes. All right, so we have a bunch of different Python files in here. I think, oh, okay. So I did actually save this in the form of hello.py. So if you type Python hello.py, you just see nothing happens. It's because we're sitting around. But I now know that on port 8082 on local host, I should be serving a webpage which will talk the HTTP protocol. Let me get a web browser up. Huh, sorry. So local host 8082 hello. And if I go to 8083, there's nothing serving there. Says I don't know what to do. Let's go back there. Hello, doesn't do anything really interesting. But at least you see the initial functionality. Okay, we can do something a little bit more interesting which is in this HTTPd.py. It's sending something a little bit more interesting where we can actually look that over now. We can actually see what the functionality is. One of the things that does happen is when we get this call, we're gonna print out to the command line some interesting statistics about what's actually being received by this web server. So let's take a look at that code. Let me kill that with just a control C. So it's similar to what we had before. We've set a name main. So this is what gets called. If I wind up starting this from the command line, looks very similar to what I had before. I'm saying deal with a request and send it to my handler. Again, I have a do get and you see that I need to send back. I need to sort of talk the HTTP protocol well. If I don't do it, then there's some defaults of what actually gets sent. But what I initially wanna send is that response. So I wanna send the 200, which is just the okay, I hear you, I hear your request. I'll then send back some header information. So I start sending the headers. I'm gonna say I'm gonna send you a text and it's of MIME type HTML. I'm gonna then send some actual HTML in this W file write. And one of the things that we'll wind up doing is in the get request, there is going to be essentially a variable that is created for me or an attribute that's created for me, which is called dot path. And you can play around and learn what's in this. We're not gonna spend a lot of time on this way of doing web pages. So I won't go into the details of it. But effectively what I'm gonna do is say, okay, what you actually asked for is something which has a path of foo bar. And I'll send that back to you and tell you that that's what you asked for. I'll do a serve forever and I'll be a little bit more graceful when I have a keyboard interrupt.  I'll do it as gracefully as I can. So let's see what that looks like. So you notice I got some nice information here. I started the server that comes from here. And let's go back. So where am I serving on 8083? By the way, should this work again? No, because I'm not serving anything there. This is a test. You access this path because I didn't actually give it anything. I'll say foo bar Justin Beaver. Is it E, I, B, E, R? Is that right? Don't pretend like you don't know. I was just trying to see who actually knows. So anyway, so the important thing here is that I get access to what it was requested. It's not just a completely anonymous request, just saying do stuff for me. Effectively now is how you would wind up saying, oh, well, that person asked for Justin Beaver, send them back, you know, Rick Roll them or something, right? Or Justin Beaver them or something. I don't know. So now you can see how you can start building in functionality and taking different actions within the response handler based on what actually got passed to you through the command line. So we saw this already just to show you again in your notes what this would look like. Okay, so Python gives you this way to do this very, very basic web server. And what we're gonna now do is kind of start building out into other Pythonic ways of doing that. Typically what we're gonna wind up having to do, however, is get some third-party stuff. So there is a nice and one of the earliest sort of third-party web frameworks that has just been getting better and better is called CherryPy. If you don't have this already, in fact, I think the end thought distribution does not come with this. Do pip install CherryPy or easy install CherryPy. I'll give you a minute to do that. And then we'll write our first CherryPy web application. I believe that there are no other dependencies or at least most of the other dependencies are already satisfied with the end thought distribution. This should be one of the easier ones. You'll know this works if you type Python and then you say import CherryPy and you don't get an error. I believe, actually, I don't remember the current versions, like 1.3. Oh, sorry, yeah, I'm thinking of Django, yeah, yes. Okay, so anyone, everyone okay with that? All right, let's go on. So there is a file in that same tar ball called CP1 or CherryPy1 and it does essentially not much more than what we had before except it's able to, with a lot fewer lines of code, sort of do all the HTTP handshaking where it's sending the headers and it's sending the 200 response for you. But now it's doing something which may actually start to be pretty interesting. So I build a class and I guess I should have made as its base class just object, but that's okay. And then I'm going to create a method within that class so I can call that class anything I want. I'll call it a welcome page. And now you can imagine each class that you build is sort of a different landing page. So if I had a log out page, a login page, or visualize this page, each one of these things could basically be set up to interact with a different call that somebody's gonna wind up making. We'll show you how to sort of route basically different URL locations to different places. But anyway, we'll now have this method called greet user and we're gonna say the first argument is self and we're going to give it a keyword name and it will default to none. So if name is not none, then we're gonna say something, hey, what's up to the user? And we should have actually just returned HTML, but for now it's okay to just return back text. But if we're being really legal about it, we should have said bracket HTML, bracket body, hey, what's up, and then close all those tags. But this is just fine. If it's not, I'm gonna say, okay, well what you should really do is you should call me like the following with a slash greet user, blah, blah, blah. And the nice thing is you can say greet user dot exposed equals true so that essentially different methods within this welcome page, you could actually develop and some of these methods may be hidden. So if one of these methods is essentially just a helper method for something else that you're doing within your class, then you don't have to expose it and somebody can't actually get access to this. But unless you do something fancy and cherry pie certainly lets you rename methods as viewed by the browser, you notice that the way we're gonna call this thing is with greet user and because greet user is now an exposed method, it will be visible by our calls at this web page level. And then we say cherry pie quick start, which just gets our whole web framework going, and then we say essentially deal with responses by going to this welcome page. All right, so let's try that. Let me kill that. And you notice, by the way, that the previous example we had exited gracefully, there wasn't a keyboard interrupt problem that showed because we handled that exception gracefully. I did control C, yeah. You can't see my fingers in there. Okay, so you notice we've got some interesting stuff. It's kind of showing us the kind of debug information that we might want. What did I just do? Oh, it's not free. I must be running some other thing on port 880. So I have to edit this. I better, oh, I have to call this. I forget, shoot. I forget. I think I have to give this some other port. Let's inspect this. So cherry pie, and then quick start. Let's say figure out what I have to do. So I have to set up a configuration file. This is a real big bummer because I didn't want to have to deal with going under different ports. I think I must have something like that open and I have too much stuff open on my machine, so I'm just gonna kill things. There is a way to do this with config files where you set up the port, but I actually didn't want to get into the details of this because again, we're gonna sort of use cherry pie as a launching off point to show us kind of elegant ways of doing this.  Let's see if that works now. You're not barfing at me, are you? You barfed at me. So for some reason, I must have already something on port 880 from another bummer. Is anyone else having problems with that? Probably not, because I've been running lots of server examples. And the problem is I don't want to have to ramp up how to actually send this to a different port. If somebody wants to Google that for me of the exact call for that. What's that? Okay, you want to tell me what it is? Config. So all caps? Oh, server.socket. Under score port, like that? I think it'll default, right? So let's try that. Okay, now it's serving on the right place and you better not barf at me. Okay, you didn't barf, okay, good. Yeah, I think maybe the notebook is screwing me up on some other browser. I'll blame the notebook. It's all your fault, notebook. All right, so we said we're gonna look at port 90, 890. And our request was gonna be get user, greet user. Thank you. So here is an example where we got a webpage back from Cherry Pie that gives us sort of debug information saying that thing doesn't exist. I didn't give it any get parameters. So it's saying, no, no, no, the way you should call me is with greet user question mark name, name equals Justin. And then it says, hey, Justin, what's up? Okay, so now it's a slightly non-trivial example of a web server. If I gave it more information, so I don't know, pet name or something. It says, no, I don't really know how to handle that. So if you really wanted to handle an arbitrary number of get parameters, what you could potentially do is just do a double star keyword arcs. So it would just sop in anything else that's thrown at it. But for now, we're making basically a structure that requires us to have only name. And if it doesn't have name, then it knows how to handle that error gracefully. Thanks for the port stuff. Okay, so we'll do something that's also a little bit less trivial, which is to actually return back when somebody goes to the index, which is what you get, you go to index.html or index.html, if you don't give it any other arguments after the base URL that you go to in its port. So now we're going to actually present a little form for somebody to fill out. And this is the HTML on the right-hand side. In green, you set what the form is and then the action and will be a method of get. So this is going to a greet user after somebody clicks submit. And then you basically give them the place where they can enter the data and you set what the name of that variable is going to be called and in this case, it's called name and then you close out that form. And I need to now expose that index. So let's take a look at what that looks like. We may have the same port problem as we had before. So let me close this out with a control C and let's try CP2. Are you barfing at me? You did barfing, didn't you? Okay, so now I'll just grab that line from CP1 and I'll put it to a different port. Oops, as you may have figured out, I'm terrible at this too. There we go, okay. And let's put it at 8091. No barf, no barf. All right, let's take a look at our web browser. We'll go to 8091. What's your name? I don't know, Doogie Houser. Hey Doogie Houser, what's up? Okay, so now we're getting the back and forth interaction and you notice what I actually have called directly is the great user with name equals Doogie plus Houser. Remember that you have to deal with spaces by encoding those in different strings that the browser knows how to deal with or the server knows how to deal with. So if I just change this to Doogie Bieber, I'd get that back and I wouldn't have to go to my original form. So let's take a look at that code for a second. So now I have two methods inside of the welcome page. I have an index, which is just where I'm gonna wind up landing if I don't give it a slash greet user. I'm gonna expose that. And when my form action is to do a get request which we'll map back to this greet user and we basically have exactly what we had before and we expose that. Here is where the configuration file should actually go. So here is where you would set up directly the port and the tutorial.conf. I'm not sure I put that in your directory. You don't actually need it for anything other than setting up port stuff. And don't worry about the thing at the end. Anyway, so we're now building up some interesting functionality within a web page. So that's kind of cool.   What you wanna do is you basically wanna give them a URL that they can go to and get into your web server. That's possible in a number of different ways. It's probably not possible if you're on air bears. Oh, I think one of these probably started up. I'm just gonna kill all this stuff. Let me start this web server again and I'll go to another tab. Why is it, why is it, why is it barfing? Something strange. Did I not close it well or something? I don't know. I'm gonna just put this somewhere else. Okay, so let me start running that and that shouldn't barf in me and it's not. And what I'll show you is what I wanna show you on the next page, which is some cool thing that I found on the web called local tunnel. It's actually a Ruby on Rails code. There's a Python version of this but if you have Ruby on Rails installed and you have something called gem installed, you can just do gem install local tunnel and gem is like pip for Ruby. And then what you do is you wind up mapping your port to some anonymous, well not quite anonymous URL and it gives it back to you and you can give that to somebody. So you can actually have somebody interacting with your web server from elsewhere. And when you first set this up, you have to basically set up some SSH keys and stuff but it's really easy to get going. So let me see if I can get this to work. Oh, why did it bail? Something's not right. Cherry pie should not be bailing. I don't have a good insight into that. I really wanted to do a local tunnel for you. Let me just try. Local tunnel 8099. So now I can presumably go to this website and assuming this didn't crash, which it did, I can try to call this thing. It crashed again. Oh, there it is, okay. What is your name, Doogie? Hey, what's up, Doogie? So you notice I didn't do this at local host anymore. I'm guessing I have another cherry pie running somewhere else in some other browser. Anyway, that's kind of cool. You can develop stuff on your laptop and then you can serve it to other people through this local tunnel. You can access that. You wanna try to hit that? 4jxx.localtunnel.com loads. Is that working for anybody? Yeah, awesome. So it should work when a number of you all come in at the same time. It knows how to do not necessarily asynchronous stuff but cherry pie will sort of naturally do multi-threading for you so it can have multiple requests happening and handling those at the same time. No, I think it's running somewhere else. I'm not quite sure what's happening, frankly, but if it works, it works, so that's how I roll. All right, so now we have a mini breakout session for you which is to change CP2 so that it asks the user for their name and their favorite color, then greet them with that color. So the way you do that in HTML is if the color name is red, you just say font color red. So if I say, what's your name? Josh, what's your favorite color? Crimson, you can actually just use the word crimson and stick it in there or you could give it one of these hex codes, six-digit codes and you can show basically the response that we had before. So go into CP2, rename it CP3 and obviously you have to add some functionality to that. So when we're actually gonna build web frameworks, what you do is you create something called templates which are, they look a lot like HTML but they have kind of variables and so if you know kind of the structure of the webpage that you're gonna present to somebody but you don't know what their username is gonna be, you don't know what their favorite color is, you can actually leave like the word red, you can leave that as a variable and then you more or less use your templates and you add the variables as need be to those templates and you render them. But for now, this is one of the things with Cherry Pie. You can use templating but for the easiest stuff you would just write the HTML yourself. Just for the people that are listening in at home, that was not the notebook. That was an errant CP2 Python running from last night. We killed it though, so now everything should be okay. Yep. Anyone have any different approaches? That's pretty much how to do it. So it's really only just changing a few lines just to summarize. You're not changing anything with the arguments or the keywords of the index method of your welcome page, but you are changing the form, the actual HTML that gets sent back to it. And here what I said was name equals fave color instead of color, but you can call it anything you want. So if I called your code with fave color it would wind up crashing, but that's fine. And so you're now sending two different variables effectively to the server and it's colorizing what it sends back to you just with the font color equals. And you could do this with a cascading style sheet, et cetera, but this is the old school way of making color. You could even do blink. I think that still works in some browsers. Were there any questions? What's that? They left it in HTML5, just as a shout out to the past. Yes, the no really long, well so if you'd have to modify it so that you'd say if name and then you go in and you do something. But if I gave you like a fave color, so if I actually filled in this line but I left that empty and I click submit, it should probably, I think it gets submitted back as a none. Some browsers may submit back an empty string in which case that's not none. So probably a way to catch that would be if name in brackets, the list, empty string, common none. If it's in there, then you'd wind up catching it and you say, hey dude, you should tell, this is how you're supposed to call this thing. And you could also catch the fave color or you could do a lot more error checking around that. Is that clear? Like if I went into a different browser, how would it look? Well some things like the font size will be set by the defaults of the browser. But you could send back a, you could certainly send back a, I want you to be this font size in a CSS. That kind of thing would be a browser dependency and this is why you'd get paid big bucks to write code that works on like everything from Windows 3 to Chrome, whatever, right?  You're on the internet, we can't use that word. There is no such thing as boring on the internet. My camera. There's a lot of that other stuff. What? Python doesn't care, so Python is getting, so your web server is taking back this request and it's just interpreting it and it's sending it to the application part. I think if you develop it in a couple different browsers and it seems like it works for you, that'd be fine. I don't think there is a standard, for instance, of when you have a form that's empty, how you deal with that. And it's up to the client, which is the browser, to decide how it's sending back that information. Why aren't they uniform? Do you know anything about the companies that are building browsers? They don't like each other a lot. So there are standards that they are all sort of adhering to, but that kind of thing of how do you treat, so of course they're speaking the correct server response standards, but how you treat the payload and how you treat the content of what you send back and forth is not at all standardized. So you just have to catch it. Another way to do it if you really wanted to force it would be to build a little JavaScript that you send on your index page, which you would send basically as part of your HTML payload, and that would do the checking of on click or on submit within your form. It would go and it would look at each one of those things and it could pop up a little thing that says, oh, you forgot to fill out this first one and refuse to actually go through and submit. So what you try to do if you're trying to build lots of user interface stuff where you have form interactions is you try to build as much of the error checking on the client side, but in the end, especially if you're doing like sensitive transactions, I could easily defeat all the JavaScript stuff and formulate a get code or a get URL that is not compliant with the stuff that you're supposed to be checking. So you almost always want to do both. You want to sanitize the input as much as you possibly can on the client side and generally you do that with JavaScript. And then on the server side, you just have to say, I got to make sure that all this stuff is okay too. Johnny dropped tables, exactly. Okay, so I've started talking a bit about frameworks and applications. I thought I'd introduce you to this concept of a web server gateway interface, which is pronounced whiskey, which sounds like whiskey. And if you had a lot of whiskey, that's how you actually pronounce whiskey. So this is the top level diagrammatic approach to how you actually build your Python applications when you know that they're gonna wind up being sort of forward-facing at the server level. So whiskey hasn't been around forever within Python. It's decidedly like a Pythonic term. It was put forth in one of these Python extension papers 333 because no one knows how to pronounce whiskey. Sometimes people just call this 333. And there's a lot of discussion about what this actually means, but effectively it allows you to write the applications. So the actual stuff that's gonna do things like take in your username and push back out some HTML that's colorized, that's the stuff that you wanna spend most of your time on. You don't wanna have to spend a lot of your time on everything around it, all the web stuff. You don't wanna have to take care of errors when people go to the wrong page. So whiskey applications are callable Python objects. And they're always passed. And so basically this PEP set up the structure of what whiskeys are we're gonna look like. They're always passed two arguments. It's a whiskey environment variable. And then another, you pass it a function that is used essentially to do the responses back and forth and allows you to do asynchronous responses. And you'll see that in just a second. So what are some things to say here? In the whiskey environment variable that you send it, that is where you encode what it is that was passed to the browser. So if you're doing a get call, it says the whiskey type call equals get. You get a lot of this information in the form of what amounts to more or less a dictionary. And the nice thing is it abstracts for you as a user when you're writing your applications what framework is providing the server layer for you. So I can write an application.  It allows you to actually just write code that's more about the functionality and less about the framework itself. So I wanted you to see that there is a difference between web servers and all of that stuff and then the actual core functionality. And what you really wanna get to is thinking about these Python web apps. And it knows how to sort of talk back and forth to the web server through a standardized protocol which is whiskey. So if I come in from a client and I'm going to post to slash login. So it's local host slash login. I'm gonna now post, I'm gonna post my username which is foo and my password which is bar. The server is dealing with that request. It's dealing with the initial response saying, okay, sounds good. And now what I'm seeing inside of my little whiskey application is essentially just a dictionary. So there's a request underscore method which is a post, the content length is 25. There's a whiskey.input string IO username, blah, blah, blah. So I have to be able to parse this stuff out within my application. The thing that's really nice about all this is there is a standard where all of the Pythonic web servers need to speak whiskey. And so if you write whiskey applications at some level you don't care where you wind up deploying it or how you wind up deploying it. So this is some text that I stole from a talk that sort of goes through and shows sophisticated examples of whiskeys. But the point is that if I build a whiskey compliant application then I can deploy it in servers that are good at different things. So if I care about a server that's really good at speed and performance, you can deploy it in that. If you want something that's easily extensible and it's written in pure Python, you can do that. I don't mean to proselytize about it but it's kind of a very nice way of thinking about writing Python applications. And I've given you some references. There are some negatives to whiskey as you might imagine and there's some naysayers. This is a very long blog entry about this that a lot of people point to. So if you're really interested in understanding that architecture, you should look at that. Then we just show you quickly what I mean by an application and the fact that it's agnostic to the server implementation of that. So at the top is a whiskey Python application and you see it again takes two. Arguments one is the environment and that's again that dictionary that I showed you.  Here we've called it start response. And your job is to essentially call it with the initial responses if you want to and then you wind up yielding whatever it is that you wanna come after that. So in this case, I'm gonna wind up showing whatever is in the environment variable that I got past. Now in this main, and I didn't give you this file in your directory, you can get it linked from this bitbucket.org. Now depending on how I wind up calling this thing, I could use the Pythonic way of dealing with whiskey and there is something within the standard library called whiskey ref and you create a simple server out of that and now I'm gonna serve it on some location and I wind up calling it with a make server and then I serve forever. Different web servers will effectively have different ways of starting things up, but they're all more or less using the same application, right? So I can use cherry pie, I can use the standard ref, there's something called workzag and there's literally dozens of these things that you could wind up using. The point is you should care mostly about the stuff at the top and not so much about how you actually deploy this. You notice this is doing a yield because any whiskey server which is compliant with the whiskey protocol is basically saying to the application, what should I do next? What should I do next? What should I do next? And remember at the very start of the course when we talked about and we introduced this yield, remember yield was considered a temporary return. So every time this application gets called by the server, it's gonna say, what should I do next? I'm gonna yield this pre, what should I do next? Okay, I'll do this printing, what should I do next? I'll do this, right? And that actually allows you to sort of have asynchronous read and writes. And there's where you would deal with your get, your get's and your posts. Any questions about that, if I move on? All of this kind of connotes the idea that there are several different solutions to creating web pages and serving them. But one of the things that you've probably guessed at already is that there are some really big nice third party apps that do a lot of the heavy lifting for us. So we don't have to get into the details of maybe even dealing with multiple different users and the fact that some users are in certain groups. Maybe we want large code bases to handle the fact that we wanna build web pages and we want them to be essentially battle tested from the beginning. We don't wanna have to deal with all these different error cases and all these different cases when a string comes back. We want form checking automatically for us and all that stuff. So this is where web frameworks come in. And the idea here is that we're supposed to not spend a lot of time thinking about web frameworks because a lot of people do this. It's big business even in the Python world, especially in the Python world. So some of the things that we would wanna do is query database B for the activity of some user. We wanna check that a submitted form field is of this type. So this is, we wanna actually ensure that we don't get back some empty string. And if we do, we wanna throw a graceful error. We wanna basically do whatever it is that you'd wanna be doing on the web to present your data, to interact with your data and to collaborate with the people that you're doing the actual work with and for. And the whole idea, of course, is that there's a lot of people who have put a lot of time into these frameworks. So the idea where the framework is that you are essentially using the tools that each one of these frameworks has used and built for you and you kind of work within that structure. So if you've got Legos, you build a Lego house, if you've got some other building blocks, you use that. So one of the things that is pretty clear is that, as I said at the beginning, we all have Apache and that is a web framework. Probably most of our interactions throughout our lives until the last few years has been knowingly or not with some Apache server on the back end. And as you can imagine, Apache has a plugin that allows you to basically call directly Python code. That's called mod underscore Python. Unfortunately, it's not compliant with Whiskey, so you can't write sort of a generic application and mod Python is essentially not used anymore. So what I used when I first started building, you want to write Python code, right? And you want to do stuff and then at some point, you're like, oh, I'd love to expose this function to the web, right? We did a little bit of this with the XML RPC stuff, but now you want to do this in the context of a web browser. There is something though called mod Whiskey, which is a plugin for those of you that are using Apache, you can basically build your Whiskey Python applications and then more or less restart your Apache server where you've uncommanded out a line, which is basically mod underscore Whiskey. It allows you then to more or less go into and actually interact with a .py file. Most people are, for whatever reason, not using this, because this is essentially an add-on to Apache, what people I think in the sort of development world and in the commercial world really think about sort of deploying their own framework. And there are a lot of frameworks to choose from. These are just some of them, actually a small fraction of them. There is an extended talk from PyCon from Australia from last year where some guy is basically going through and just rating all of these based on their usability and essentially what he needed for his tasks. There's some really major Python frameworks that are mega code bases and you'd use if you're gonna have 10,000 users or something. That's like the Zope and the Pylons and the Grok. But there's some that are sort of called micro frameworks or mini frameworks that are also easily deployed. They have some drawbacks, some limitations. They don't scale as well, et cetera, but they're starting to gain a lot of popularity. And those two are bottle and flask. You can guess, I guess that's the answer to Whiskey or something, right? You gotta put it somewhere. Actually, I don't know if that's actually been said on the web, can you Google that? Because if not, I'm gonna coin that term or that idea. You have to know where to put your Whiskey. It should be in Bottle or Flask. So Bottle's nice because it's a web framework that has no dependencies other than the standard library. That's very cool. I think, however, it is not Python 3 compliant and there's some moves to try to do that. Flask does have a few dependencies and it's built on some code bases that have been around for 10 years or so, so it's considered intrinsically more battle-tested and I think it is ready for Python 3. I might be getting those two mixed up. It's using this thing called Workzong and Jinga 2 for those that care about that. And they're both very beautiful. If you go to those two web pages for Flask and Bottle, you'll see some very nice sort of Hello World examples and now it makes extensive use of decorators. What we'll introduce today is Django, which is really kind of the de facto large web framework that people use in the real world. And it basically gives you all the functionality that you'd wanna have in any modern web framework and it's all completely Pythonized. And we'll see some of the nice things about that in just a little bit. So we're gonna build our Hello World application here and I think some of you, we've already introduced virtual M, but if not, what you may wanna do is you may wanna sort of do all this stuff within a virtual environment. So these are the calls you would need to get going in that. This is sort of optional effectively. You'd wanna get going in a virtual environment, which we'll just call Django. I'll let you, I think you guys all have the lecture notes, so I'll let you just sort of cut and paste that in if you'd like to. But to get going, do a pip install Django or easy underscore install Django and see how that goes. That should install 1.3.1, I believe. Yeah, yeah. So, I mean I can give you like a 10 second introduction to virtual environment. The idea is that you wanna build different Python applications, one may be your web application, one may be the thing that interacts with your old Fortran code, another one may be one that you use in your laboratory to interact with your little gadgets and stuff. And each of them may have different dependencies, like you may be using some legacy driver that requires you to have an old version of something, right? Virtual environments allow you to basically have completely firewalled off Python environments and you can have multiple different Python environments. So typically what you would do is you'd start a virtual environment for like when I'm working within the class, right? And I wanna be able to install stuff, but I know I've already got it somewhere else and I know I've got working versions of older things that are really battle tested, but now I wanna try, let's say, a development version of IPython and I wanna show you that in the class. I can go into a virtual environment, install that and then it's only accessible and only viewable by me when I'm in that virtual environment. So for doing Django stuff, you may already have been developing some web app and you're comfortable using Django 0.99 and Django 1.0 breaks a couple of those things and you don't wanna mess with that. Did I get that, right? Okay, anyone have any problems with Django installation? What's that? Oh, yours is still installing. We'll give it 10 more seconds. So in your environment, after you do that installation, there should be something called Django-admin.py and it's got a bunch of different things you can use. We can do a start project, which will basically create a structure of a project that we're gonna wind up interacting with and I think in the tar ball that I gave you, there's something called hello, which is a directory and in that is some of the very basic functionality of a new Django project. So you can take a look at that, but we can try to start that up together. By the way, Casey Stark is here and this is his slides and his worked example. So I'll take credit for that until it doesn't work and then I'll ask him to stand up and talk about it. Okay, so what's that? Okay, so I have something called Dhello and you should see it looks like this. We'll look more at that in just a bit, but let's see if this actually works. Okay, so I just ran that. You should have no barfs on the command line and you see it starts something called myhello. Ah, it wrapped myhello, okay? And it should look just like the Dhello, okay? All right, so let's sort of start looking at what these different files do. So the first one is URLs.py. You'll see something that looks like this and views.py, you'll see something that looks like this. This should look a lot like, in fact we're gonna want to be editing things to look like this in just a minute, but what you notice here in views is that we're pulling in this HTTP response and this is now a file that we're essentially now, or a file type that we're now sending strings to and this index is gonna look a lot like what we had as our method within the welcome page when we were doing cherry pie. We're gonna wind up writing a bunch of things that will interact with the web server and the URLs are more or less just a lookup of how I go from what I get at the URL level to where I should go. So now this is saying basically match anything, so this is a regular expression string, so it's saying basically match anything that isn't written out so if I said greet user it wouldn't match that and this would just say okay this person wants the index.html, go to views and look at the index and deal with that response in whatever the way that index says that you should. So that stuff is not actually there out of the box in the code that you just created. So if I look at URLs, essentially I have a bunch of lookup patterns which is telling the web server okay when this person goes to admin slash doc show them some documentation that's only available to admin users. Go here, I wanna show them some home page, I wanna call it home. Here I'm gonna wind up including some subdirectory. So all of this stuff is blocked out so we can't actually use it, so we actually have to change it. So we'll actually do that and I'll ask you to do that as well. Okay so let me email, let me edit this, to add that line that we had before. I think I need a comma. And you notice that we don't have a views.py, so let's make a views.py. And I think for those that are having trouble editing or something this D-hello has the necessary edits in it. So that's what that should look like and then we added a couple things. So here in this one in the D-hello, I also made sure that if I went to slash hello, that it would also map to the same place. So there's different ways to do this with regular expressions and I'm not gonna go into the details of what all these carrots and stuff mean. We've seen regular expressions before but this is now the structure of my code and now we're gonna have to run our web server to actually start serving this stuff out. Are there any questions so far? I'll go into some of the details in a few slides but I just wanted you to all get comfortable editing and creating the right files for a Django project. So we're now in need to run python-space-manage.py, run server and you notice that there is a manage.py here and this is just doing almost nothing. It's just looking to find a module called settings and it's dealing with stuff gracefully when it's not finding it and so settings have a whole bunch of interesting things in here that we're gonna wind up playing with later on. You can set up the sense of who admin people are, what type of databases we're gonna wind up using and then actually whether you expose some of the admin functionality and then you're gonna wind up having all the applications, these are now this concept of this web application, you're gonna wind up putting in here and we'll wind up actually adding those things directly and then there's some logging information and all that stuff. So this is a pretty big code base. It takes a long time to get to understand what each of these things is doing but recognize that the sort of out of the box template example that you wind up getting sort of sets this all up for a default and you can run it and it is as battle tested as something like Apache might be considered. Let me just take a look at the settings of the D-Hello and you see in the D-Hello, I actually uncommented out these functionalities of administrative privileges. Okay, well anyway, let's start the web server without further ado. Let's see if that works. Ah, okay, I got no errors. Validated all my models. We'll see what validation of models means in just a bit. Here's my Django version 1.31 and now I can go to my web server up here. Hello world, right? So we built our first hello world application. If I wanna use local tunnel now, I could give you all the ability to go in and start hitting my web server, right? Let me give you sort of one minute for those that are trying to catch up to this to try to get that going as well. If you have problems raise your hand. Is anyone getting any errors? Effectively, what you do is emulate what you see in URLs.py in the D-Hello directory and then also emulate what you see in the views.py in the D-Hello or you could just go to D-Hello and type python manage.py run server. Yes. Yeah. Okay, I'll do that now in the next slide. Okay. Yes, I wanted you to just see how all this works because that's like, oh man, exactly. Why am I editing this? Why am I doing this? Let me see if we can get a little bit of a big picture here. Well, first of all, you are doing at the very base level what Cherrypie was doing and what base HTTP server was doing at the very beginning, right? You are creating and running a web server that's doing some functionality, right? And how you route things around when I go to the web browser and I type in localhost 8000, then the web server says, okay, you've just requested essentially nothing, you've essentially requested index.html without actually explicitly saying that. What am I supposed to do? All right, let me go look at views.py and views.py says, oh, that routes to some other functionality that I now know how to, sorry, urls.py was where I look and then I route to some other functionality in views.py and I know, oh, okay, so I'm supposed to use this object and it's a subclass of the HTTP response and now I'm just gonna do whatever happens inside of that and then I'm done for now until somebody else requests something else of me. I mean, if I go back and I go into some other place within that web server, it says, I don't know how to match that so it's actually kind of useful to see this. I put this in debug mode so of course you don't have to put in debug mode. So the request method was get, the request URL was this and I'm looking at myhello.urls and I only knew how to do one thing which was to match nothing and now you gave me something called buy. I don't even know how to match that. I don't even know what to do with that. I mean, if it's helpful, I can go through and we can figure out how we would handle this. You wanna see if we can handle this? All right, let's try to handle it. By the way, this thing is timing out here. It's still trying to go to that URL because I killed the cherry pie and yet it's trying to basically go to a place that's being mapped to something where nothing's being served from. Let's take a look at what we had up in here. So this is in the dehello and what you see here is I actually created two mappings so I created a hello slash. So let's just copy that and we'll change that to buy. Let me kill this. So I'm gonna go into URLs and I'll now map something else that I can do. You can pick anything else but convention is to call it views and then actually every sub directory may have its own views associated with it. So what you could do is when you get a slash something and it's buy, then you've got a directory called buy and you could just have its own views inside of that. So the top level directory says, I don't even know what to do and all the sub slashes of buy but I know that somebody else is gonna take care of that view for me. So you can sort of pass views down the road if you want to. So let's create a new functionality called buy and we could call it buy buy just to convince you that it doesn't have to be the same thing as what that URL is, right? I can map it to anything that I want. Yes, which I think is you could also call that, no URLs.py is something that's looked up by the main web server and you could even change that name but again, it's convention to keep all that stuff. So now we need to change views and we need to create something called buy. Bye bye. Okay, so you would just keep on building up your functionality by, oh, I really like to have a login page or I'd really like to have a view file page or download file page and then you have to know how to do that within the Django framework. So inside of URLs, I created a new mapping which when the user went to buy slash, I sent it to views.byebye. I could have called it anything I want. I could have put it in something else called buy and then inside of that file, buy.py, inside of that file, it could have done stuff. It's up to you to organize it how you want to. It doesn't have up to your... Yeah, that's right. Exactly, yeah. You'd like to actually just not have that as a string. It makes sense to actually give it essentially the function, right? But yes, it's doing an evaluation. It does some look ahead to make sure that that file is there and blah, blah, blah. You can do that. You can do it without strings? Oh, okay. Yeah. Oh, right, so that tells you that you could have your code base somewhere else. It wouldn't have to be in the same like sort of subdirectory of where you're running your project. Okay. Well, we're not gonna build something that complicated for now, but it looks like you can do a lot. All right, anyway, so just keep it like the way it is now with the strings. All right, remember when we talked about databases and we talked about our interaction with the databases, we wanted to have this concept of a model view controller. And we talked about this also in the concept when we talked about GUIs. And when we're, especially when we were talking about Chaco and Traits, particular Traits. We talked about the model view controller paradigm, this idea that you have some base sort of understanding of the relationship of your data to other pieces of the data, and then a view of that data.  We wound up being able to view that directly. We created a view and we popped up a GUI. Now, effectively what views are doing is it allows us to look at not just models, in this case, essentially high level abstractions of our data, but also different views on different modules that are gonna wind up producing and creating different functionality for us. That's maybe a little bit hand wavy, so I wanna sort of actually give you some examples of what it is that I'm talking about there. But anyway, the way Django does stuff is with the model URL view template idea. So templates create effectively different types of views on our data. And then the routing stuff which happens with URLs.py that happens in that file. Okay, let me get into some details so that you actually see what it is I'm talking about. When we talk about models, we talk about the sort of conception of data. And if you wanna think about this in terms of a relational database and say SQLite3 or MySQL, we might think of the concept of tables. And now when I wanna connect models together, I'm effectively creating foreign keys that are gonna link back to primary keys of other tables. One of the very nice things that Django does for you in the same way that SQL Alchemy does this for you is it has an object relational manager that will abstract the actual database for you. So you will have an SQLite3 database or a MySQL database or a Postgres or a SQL server or whatever. As your back engine to store all of your data, but it allows you to think about that data in exactly the same way, irrespective of what your database is. So models is where the data goes and the classes that model the data objects are the ones that you'll be using inside of your application and we can store in whatever database we want. So here is very similar to what we did with traits. Now we basically will subclass something called models.model and we're gonna create some object called the lecture. And one of the things that we'd like to know about a lecture, well, whatever its name is, what semester it's in, the number of students who are associated with that lecture, et cetera. So here you're basically creating attributes of this model. And we've seen this before. So hopefully you guys are comfortable with it. The nice things that we saw in traits is the same nice things that we saw that we get out of models with Django. And in particular, it makes sure that we do error checking and so we do type checking. So if somebody tries to add a new lecture to our database, if its name is not of type car field, then it will barf and you'll get errors. So people can't pollute our database and it makes sure that you basically have everything in the way that you want it to arrive. And so there's a lot of different types of types and I'll give you a URL for that in just a couple of slides.  This is for internationalization so you could have lookup tables that allow you to basically very seamlessly show your same website in different languages. You don't have to have that. It's just kind of nice that Django deals with internationalization stuff for you. Again, probably you're not gonna want that in your real world applications because you guys are not building massive frameworks for people, but if you are, Django is sort of one of the places you'd like to go. So just like in traits, there is that concept of the relationship between objects and models.  We have a concept of a car. It's going to have a couple of different manufacturers so it might, it'll point back to the foreign key of our manufacturers. Here I didn't give it any attributes so it's gonna have maybe some ID number which gets auto-incremented for me and I haven't told you about rows or columns or who's dealing with the database stuff. Django's doing that for you. Again, it abstracts the sort of SQL level database interactions for you to be able to basically just play with models. So here's the company ID and I'm gonna wind up pointing back to the Cadillac company. I should have updated this because some of these companies are like probably bankrupt by now. And I've already mentioned the ORM that Django provides for you. Essentially, I think is it even using SQL Alchemy as it's ORM? Okay. Yeah, okay. So anyway, you've already seen SQL Alchemy. You know why ORMs are very nice. They're nice. Okay, so again, let's go into what the URLs are doing. This is where you can sort of pass things off so we can set up very complex URL patterns and I won't go into the details of what all this stuff is doing, but you can see that we can get with, we can get with these various regular expressions, the ability to sort of send different things to different places. So I have an issue and I've got some issue number and I wanna delete that issue and I'm gonna send that somewhere and I'm gonna send it to something called delete. Here I'm gonna send everything that says issues, sorry, everything here I'm gonna send to issues. It's all structured, it's all hierarchical so you can send things into other directories and have those other directory URLs deal with that sort of stuff. And then the views. So this is just sort of an example. We're not gonna actually wind up using this stuff, but here is where you would actually have the functionality that says, oh, this person requested this photo and I've got, I either got this or I don't have it so I'm gonna send you back the results of that. And then templates, that's the new thing that what I haven't shown you yet. Templates look a lot like HTML, but if I'm going to reuse a page over and over again instead of in my Python, more or less rewriting HTML, now what you do is you basically write templates that look a lot like HTML except they have variables. And when you wind up rendering this template as HTML, what you basically do to the renderer is you pass it the template you wanna use and you pass it these variables. And so you might have content and if it's none, you might say, don't show it, I don't have any content. So templating languages, you have to know HTML and you have to know the way in which each one of your web frameworks does templates. I think there's a convergence at some level into only one or two different templating protocols. So templates generally look really simple.  And the content is going to be whatever content I wanna send it. That's the stuff that's gonna change from page after page. If I have a new book and I wanna render stuff like that, I change the page title when I wind up rendering the HTML. And so there, what you're thinking of is somebody's made a request, they say, give me the next book in your library and you pull over some information, so you're gonna do a query on your models. You pull over the next one and it's got a variable or an attribute called page title and then when you render your template, you just render that, right? And so you get to reuse HTML with this. Now, why is it that we're actually going through all this trouble? Well, I think probably for any of the web applications that you all might wind up making for your own work, something like Cherry Pie or Flask or Bottle might be enough, but one of the great things that Django has is all of these third-party plugins that allow you to do high-level stuff that you don't have to build the full functionality. Effectively, they just give you the ability to do that. So usually registration, like dealing with passwords over the internet and dealing with passwords that are too short or too long, somebody's built an app for that, right? Doing tags on blog content. Somebody's built a little applet for that. All these things already exist. So one of the nice things that you think of when you're thinking about building a big Django application is pulling in lots of functionality from all over the place. And then the thing that's new that you do is just a little part of the full application that this Django web framework provides for you. So it's plug and play. So this is something that Casey had in one of his applications. These are all the codes. This is just the code that he wrote. So this points effectively to Python code that he wrote. Everything else that he needed, he just pulled in. So there's Ajax validation, there's pagination, there's sign-up stuff, there's analytics, you know, you can pull in a great deal of this functionality without having to write this stuff yourself. And that is, I think, one of the best selling points of building your application on a massive framework like this. It's a little bit painful to understand the connections of all these different files together, but under the hood, you're building some functionality. You're dealing with data in the concept of O model. You're creating views that point you from some URL to somewhere else. And you're reusing all the stuff that people have already done for you. Okay, so for the breakout, we're gonna try to build a Django framework that has some functionality of views. And what we're gonna try to do is build some sort of journal where you might sort of effectively have blog entries just by you, but you could add other people into this. So, first of all, there is something called, I think, journal underscore skeleton in the tarball that I gave you, is that there? Yes, so it's already got some of the starts for you. And what you'll notice is that there is a directory called articles. And inside of that will look a lot like the top level directory. It will have a views and a URL. And in that, it's gonna have something models. You're going to wind up editing those models. That's the first thing we'll wind up doing. And those models will be, we wanna add all of these attributes to this model. So we're gonna have a title, some sort of slug comments, or essentially, that'll give us our final URL. The author name, which will be of type user, when this is published. So that's gonna be some sort of date time object in abstract, which will be some sort of blob or texturing a body. Go to this URL right here and look at the different types of attributes that you can have inside of models and pick appropriate attributes. And the good thing is, while we could build in a bunch of different ways to and build web pages that allow us to add and delete each one of these articles or log entries or whatever, however you wanna think of it, we'll just let the admin do that. And so what we'll wind up doing is commenting out a few lines in the admin in the settings that allow us to interact with our models as an admin user and change things. I'll come back to this in just a second. Let me just show you what this looks like. So the nice thing is, once you get going, you can have a models and inside of those models will be articles and then I'll be able to add to those articles and essentially add new entries. So how do we do that? Well, we can have a foreign key of type user and we basically pull in from Django contrib auth models, this concept of a user. And so we let Django just deal with the authentication of users and storing their email addresses and whatever other information we might want for them. So you're gonna wind up creating an author attribute within inside of your articles models and it will be of type models.foreignkey user. That's one of the things that you'll need to do. And I think I've already given you the templates to render the site so we can actually look at stuff. So here's my site. I've got a variable called head title and I've got some body that I'm gonna wind up showing and then the nice thing is Django is extensible so when we template this site underscore base.html what I do is I basically pull in the full functionality from this thing and now I can add more stuff myself. So the head title, I'm basically writing directly will be all articles. I think I've already given you those templates. Is there a templates directory in there? So you don't have to worry about that now. You'll need it for your homework so I just wanted you to see that. Okay, so get working on this. I'll give you about 20 minutes and we're all around to help you. So the first thing to do, obviously the documentation is your friend and you should go look at the Django documentation. What you should do is interact with models.py and add some attributes there and I've given you on the previous slides what I'd like you to add. Title, slug, author, published, abstract body.  And I've given you a journals.tgz, I think. If you try to unzip that, it is encrypted and I will tell you the password at the end but that isn't solution. You already have that already. Try to crack it if you wanna spend all your time on that.


Guidance is such a practical means. Guidance is so tied in with the miracle that it is very, very, very much a part of the pathway of a Course in Miracles. The guidance is miraculous. It is what the miracle is. The miracle is just being aligned with the Source and feeling that wonderful, warm, glowy kind of feeling. Regardless of the specifics, you know, the specifics may come to you like, do this, do this, you need to stop doing this, but that warm, glowy feeling of connection is the miracle. The Holy Spirit can use any of the symbols of time and space to reach the mind, not necessarily an audible voice, but if you have the prayer of your heart say, reach me. Please help me. Reach me some way. It can come in many different forms so guidance is not limited to hearing an audible voice. Following guidance is something that is so powerful and so effective and it is something we can all do. No one has the power to stop us. Keep asking and keep listening and keep following. If two people are genuinely attuned to the Holy Spirit, they will hear the same thing at the same time. You see how that points more to, there's just one spirit. It's the spirit of love. It's the spirit of joy. It's the spirit of connection. It's the spirit of we're all in this together. It's the spirit of we're all the same one, whether we recognize it or not. We're really all just one self in God.