Archive for the ‘geek’ Category

Python and AJAX tutorial for beginners with web.py and jQuery

Note: I’m always surprised to see this at the top of a Google search for ‘ajax python’. It has been three and a half years after all (though I guess that’s how these things work sometimes). I haven’t been following the state of Python web development at all, so this code may be badly out-of-date, simply not work, etcetera. If you’re just starting out on your Pythonic path, I feel like my best recommendation is to Google on, my friend, Google on…

– George
14 September, 2013

As a result of following along with the NYU class Reading and Writing Electronic Text I started to get into using Python for web apps — this, along with learning JavaScript, is something I’d been wanting to do for a while.

I’ll try to summarize what I’ve learned over the last week or so. What I was looking for was a simple way to get into this stuff, and as I discovered it’s not so simple, for a hobbyist like myself, once you combine two or three frameworks in a project. Maybe this post will help someone else in the same situation.

Python already was the choice for the server-side script. What I then needed was a way for the browser to communicate with the script. There are literally dozens of ways to do this, and the Python wiki has a good page on web programming. I also found this conversation springing from a post by the BDFL at Artima a good read, though it’s a few years old now.

I decided to keep it simple (though I still want to learn frameworks like Django, TurboGears, etcetera at some point in the future). I considered looking into using CGI with Python, but then I found web.py, a lightweight web framework, and liked it immediately.

The web.py framework can run its own basic web server or interface with others such as Apache or lighttpd. It includes a simple HTML template language that basically is just HTML + Python. It works with databases or not. In short it has all you need to get things running quickly.

So I had the server language and the web framework. All that remained was the client-side. Rather than rolling all the JavaScript myself I went with the jQuery library. I had heard some good things and it looked good when I checked it out.

OK, first let’s look at the basic project structure. Note that I’m using web.py’s templates, though this isn’t required:

- webpy_jquery_ajax_tutorial
  - static
      jquery.js
     tutorial.css

  - templates
    tutorial.html
  app.py

Within the project directory we include the static and templates directories (required terms for web.py). Files such as CSS, JavaScript, images, and so on are put in static. HTML templates go in templates, and everything else can go in the main project directory.

Our app.py is the web.py script that controls our app. It looks like this:

import web

def make_text(string):
    return string

urls = ('/', 'tutorial')
render = web.template.render('templates/')

app = web.application(urls, globals())

my_form = web.form.Form(
                web.form.Textbox('', class_='textfield', id='textfield'),
                )

class tutorial:
    def GET(self):
        form = my_form()
        return render.tutorial(form, "Your text goes here.")
        
    def POST(self):
        form = my_form()
        form.validates()
        s = form.value['textfield']
        return make_text(s)

if __name__ == '__main__':
    app.run()

Other than the simple echo function defined for this tutorial, there are some critical parts to the code above, so let’s look at it more closely.

First of all web.py will map the URL to classes in the script. That’s why we have urls = (‘/’, ‘tutorial’), which says map the root URL address to what happens in the tutorial class defined later.

Second, if we’re using templates we tell web.py where to find our HTML templates with render = web.template.render(‘templates/’). The my_form creates an HTML text entry box that we’ll render later. We give it an empty string for a name simply for aesthetic reasons (otherwise you’ll see it on the HTML page).

Finally we define the tutorial class. We create the GET and POST methods ( web.py intentionally requires this)to define the behaviour of our web.py app in response to browser client requests. Looking at GET:

    def GET(self):
        form = my_form()
        return render.tutorial(form, "Your text goes here.")

Defining form = my_form() gives us a copy of the form instance we created earlier. The method render.tutorial() returns a rendering of the tutorial HTML template (using the same name tutorial here in the class definition, the call, and eventually the HTML template are important here!), passing the arguments form and the text string to the template at the same time.

Now for POST:

    def POST(self):
        form = my_form()
        form.validates()
        s = form.value['textfield']
        return make_text(s)

Again using a copy of the form instance, we automatically validate it (simply because I haven’t learned how to validate forms yet!). We can then access the value of our textfield box with form.value['textfield'], and send it to our echo function defined earlier.

That’s all you need in the app.py, now to look at the HTML template (located in the templates directory):

$def with (form, text)

<!doctype html>

<html>

    <head>
        <title>Hello</title>
        <link rel="stylesheet" type="text/css" href="/static/tutorial.css" />
        
        <script type="text/javascript" src="/static/jquery.js"></script>
        
        <script type="text/javascript">
                                jQuery(document).ready(function() {
                                jQuery(".button").click(function() {
                                        var input_string = $$("input#textfield").val();
                                        jQuery.ajax({
                                                type: "POST",
                                                data: {textfield : input_string},
                                                success: function(data) {
                                                jQuery('#foo').html(data).hide().fadeIn(1500);
                                                },
                                                });
                                        return false;
                                        });
                                });
        
                        </script>
    </head>
    
    <body>
        <br>
        <form class="form" method="post"> 
        $:form.render()
        <input class="button" type="submit" value="send"/>    
        </form>

        <br><br>
        <span id="foo">$text</span>        
    </body>
    
</html>

(we’ll see how WP handles the syntax styling with that…)

There’s a little more going on here so let’s take it one step at a time.

In a web.py template the first line of the file defines what data is passed to the template by web.py. So here we have $def with (form, text), which as you’ll recall parallels the call we made in the GET method above. So when a client requests this page, those arguments are passed along.

The web.py templating language uses ‘$’ symbols to denote Python expressions (and the arguments passed into the template). To use an actual ‘$’ just escape it with ‘$$’. Note that it’s a popular habit in jQuery to also use the dollar sign to mean the jQuery object, but we can get around that conflict simply by using ‘jQuery’ instead in the JavaScript. There are other ways around that but I like this method for its simplicity and how it feels the same as Python namespaces.

The head tag of the file is nothing special, it just tells us where the CSS and .js files are located (though it’s important to web.py that they’re in static as explained before). The JavaScript that follows needs some more explanation though:

        
        <script type="text/javascript">
                                jQuery(document).ready(function() {
                                jQuery(".button").click(function() {
                                        var input_string = $$("input#textfield").val();
                                        jQuery.ajax({
                                                type: "POST",
                                                data: {textfield : input_string},
                                                success: function(data) {
                                                jQuery('#foo').html(data).hide().fadeIn(1500);
                                                },
                                                });
                                        return false;
                                        });
                                });
        
                        </script>

It’s actually pretty simple. All this does is send what’s in the textfield box to the server when the button is clicked. If the action is successful (i.e. the server accepts the input data and returns it OK), jQuery replaces what’s in the foo element with the data returned. This uses the ajax() function — so the page doesn’t reload (it’s not really AJAX as you may realize, since there’s no XML exchanged — that’s OK, I don’t like XML anyway! ;) )

I’ll point out some conventions that a beginner should know. Saying “.button” just refers to the element with the button class (defined in our app.py script, look above). Saying “input#textfield” refers to the input element with the CSS id of textfield (also defined in app.py).

The success function passed through ajax() is called if the action is successful; the part with hide() and then fadeIn() is just a prettier way of displaying the new text on the HTML page (otherwise, it just replaces it with no transition effect).

Lastly, returning false prevents the page from reloading.

Moving on to the body of the HTML page:

    <body>
        <br>
        <form class="form" method="post"> 
        $:form.render()
        <input class="button" type="submit" value="send"/>    
        </form>

        <br><br>
        <span id="foo">$text</span>        
    </body>
    

All we do here is enclose a rendering of the textfield and button in form tags. This isn’t totally necessary, but as I discovered, without the form tags pressing return in the textfield box doesn’t activate the button (i.e. you have to click on the button). Apparently you can process keyboard events with jQuery but this seemed simpler.

An additional note about $ is that you need to use $: if the expression will contain some HTML that you don’t want escaped, as we do with $:form.render.

The foo span element is just a basic container that the jQuery function can replace with the new text.

To recap, what this does is display (from your browser GETting the page from the server) a HTML page with a textfield and button. When you enter text in the textfield and submit it (that is, POST it), the JavaScript on the HTML page runs, and replaces the foo element with the text processed and returned by the app.py without reloading the page. There you have it — basic Python and AJAX!

There are some obvious improvements to be made here. First of all there’s nothing stopping the user from clicking the button multiple times and sending multiple POST requests (probably not a desirable situation). There also isn’t any user session setup or form validation. In any case these things are next in my list to learn, but my experience with web.py and jQuery has been great so far. I’m looking forward to more!

Also, the documentation for web.py and jQuery are good, so check those out for more information on their APIs.

I’d like to thank the members of the web.py mailing list, particularly Branko Vukelić, for their feedback on an earlier draft of this post. Naturally all errors are (most definitely) my own.

If you’d like a zipped copy of the files in this tutorial, you can get it here. Run app.py and then point your browser to http://localhost:8080 (on Windows; use the appropriate address for your setup).

when you meet the code, kill the code

It’s 2:45 am…I’ve been working on this for the last 6 hours:

import re
import string

l = string.lowercase
u = string.uppercase

def fun(delta, s):
    r = re.compile(':(.{%s})(.*?):' % delta)
    p = r.sub(r'\2\1', ':%s::%s:' % (l, u))
    m = string.maketrans(l + u, p)
    print s.translate(m)
    
s = raw_input("What is the string? 
delta = raw_input("And by how many places do we change it? 

fun(delta, s)


(looks like WP’s sourcecode tags are broken, but imagine a closing quote and parentheses for the raw_input functions there)

I learned a lot about regexps and list rotations in the course of this, 80% of which I didn’t know before…however the canonical solution for the same problem here can be done in half as much code. And probably less than that (actually, not probably — definitely — like in one line depending on the language). The idea is to use slices. Slices! If I had known…but now I do.

Anyway I’m still learning the syntax basically, but it’s a lot of fun.

Oh yeah…the idea is to aim toward making a Python roguelike. (!!?!!)

Robin Hobb at the University Bookstore

Following on the last post, the other night I heard Robin Hobb read at the University Bookstore, on her tour supporting her new book Renegade’s Magic.

I brought the recorder to try it out. I was about ten feet from the podium, and the bookstore is something of a big open space so there was a lot of ambient noise. For the introduction of Robin I had the recorder in my shirt pocket (I don’t have an external microphone yet) and for her reading I held the recorder in my lap pointing up. The PA system for the reading wasn’t very loud — kind of like a little louder than normal speaking voice. The final file was about half a gig of 44.1 khz WAV.

In Audacity I cut out a few spots of the WAV file and used the envelope tool and amplify effect to increase the volume on some points, and the envelope tool to decrease some loud spots, but other than that I didn’t do too much. I still haven’t figured out how much of that works.

I did cut up the file into two parts — so if you just want to hear the question and answer you can skip to that.

Robin Hobb introduction and reading:


Robin Hobb Q & A:


I was impressed by the reading, she seems really cool.

+ sound

So I’m pysched lately because I’m starting a new thing that I’ve been wanting to do for a long time. After much reading of reviews and gazing at EBay, Craigslist &c. I went out to (local) Streamline Audio (totally nice there) and got a digital field recorder! My choice after looking at the Zoom H-4 and H-2, Edirol r-09, m-Audio MicroTrack and a couple of others was the Marantz PMD620.

Marantz PMD620

All of the super-portable recorders seem to have their pros and cons at this price level, but for me the Marantz has the features (good and basic) and the form factor (streamlined) I was looking for.

I took it out of the box and within seconds I was up and running; it’s really very easy to use, feels well-made and just solid. In the coming weeks I’m going to record some readings and talks, use Audacity to edit the files and then put the mp3s up here. In the farther future I want to experiment a little, possibly making some IF/audio games. OK!

lists now!

It so happens that I’m horrible at keeping any kind of list or ledger. Grocery list? Forget it. Balance a checkbook? In my dreams. This hasn’t stopped me from endless iterations of trying to keep lists. The joke here is that I also have a pretty lousy memory.

Anyway, you know what they say, rage, rage, and all that. Sometimes I think my sallies at list-making will, one day, catapult me into a golden age of perfect recall, or at least a list for every occasion, where I’ll never again forget a date, name, the book I read last thursday, or the author of the book I’m reading now (which just happened the other day).

I’m trying to keep a couple of lists here, of games I’m playing and books I’m reading. I won’t even try to keep them totally up-to-date, but instead I’m hoping they’ll grow by gradual accretion to record a history of what I’ve played and read lately. Already I’m starting to circumvent these lists, playing games and reading books that I’m not playing now — the horror! We’ll see, for example, if Lost Pig is still up there next year, like my Playing Now figurehead.

full magazine

I’ve been poking around lately to see the extent of the SF magazine world. This will get redundant I’m sure.

Here’s an oldish list of SF small press stuff and webzines at suddenlypress.com.

The SFWA list.

Yes, the Wikipedia entry.

Found “An Open Source Speculative Fiction Magazine Model” (though I don’t know how open source figures into that, honestly).

That got sparked by Paolo Bacigalupi’s posts on the subject, which you can find through the article linked above.

A half-fluff piece at Speculations, good enough to skim.

Anyway, so the point of this post — here is what I think would be cool: a paying online SF magazine that included IF. Call it a monthly, 1-3 IF works a month, 2-3 short stories and serials, and a weekly column of something, say around 25k words total (let’s be generous and do .05 a word, so $1250, and IF gets a flat rate of $100. Annual budget is $18k — hahahaha).

Maybe the column rotates, first week is editorial, second is SF, third is IF, and fourth is craaaaaazy.

Notwithstanding I have no technical experience to get something like that running, I would like to read something like that. And hey, this is why I get to post it on this thing and not spray it across a forum somewhere.

The magazine gets funded by general donations, and throw in a tip jar for individual stories.

It would need a web-based interpreter, so people could read it anywhere — I wonder how well a web interpreter works on a mobile device.

Quiet please (part one)

Live! straight from the…

from far away

The first time I saw the design for the Central Library, man, it was ugly.

I eventually came around. By the time of opening day, when I volunteered and got a half hour of crowd-free blissful wandering through all the floors, practically alone, I was hooked.

not so far away

I go to the library all the time, so this was my natural spot for my IFComp in Public (IFCP) day.

However I got a little too ambitious, and in attempting to sandbox an IF playing account on the ubuntu set up on my old laptop I spent way more time installing, compiling, and configuring then actually playing any IF. The sandbox is on the right.

up close

And I still can’t get Gargoyle to compile.

Anyway, I’ll work on this for the next IFCP day (November 10th). Meanwhile I’m up here on the top floor. It’s about 4 o’clock and the light is starting to come in over the sound, you can just see it down Spring street. There’s the library hum, every so often someone coughs or drops a book. Hey, that just happened. Different footsteps, shuffling feet, heavy feet, squeaking feet. A lot of tourists. Sometimes the floor shakes when someone walks by. The area I’m in is elevated, must be some kind of floating riser.

the game I am kind of playing right now is pretty good, Lord Bellwater’s Secret, at least so far.

closets

It has a nice way of opening the game even though it’s infodumping all over myself.

About this IFCP thing. Of course I’ve been having mixed feelings about the whole deal. The whole public/private line. Nevertheless it is pretty fun. I may have a strange sense of fun, I don’t know. I may end up doing this more often just for the hell of it. It does fit with the library so well, everyone here as I type this, doing their own thing in this public square/private headspace. I do quite like the idea of sandboxing an IF account and putting it on a desk with the sign, and just seeing what happens.

Interesting thing with the new IFDB announced by Mike Roberts — you can almost get a good survey of the active users on RAIF by watching the registered users count go up on IFDB. During the early beta it was steady at about 12 users — after Mike announced it shot up to about 50. These are the most active people on RAIF, say, about 40 people. The count slowly has climbed, currently it’s at 83, so another 30 people or so have trickled in over the last few days. Maybe these are people who don’t read RAIF every day. So we have about 70 active on a day to day basis. What percentage of the active RAIF users is this? Or of RAIF lurkers for that matter?

Oh well, got to do what we can before the sun burns out.

close enough

o.m.g.

Courtesy of the fine stylings of Renga in Blue, I give you:

The B-Game Competition!

If you don’t have a sense of humor, don’t click that link! Maybe not work safe.

good writing software for windows

Every so often, when I’m a hater on MS Word or kind of blah about OpenOffice Writer, or not down with le SciTE I do a search for what writing software is on the market. Usually I’m a little disappointed at the end. One time I found Liquid Story Binder, which is good, but for me LSB is just too much. Maybe at some point I’ll go back to it.

Imagine my surprise tonight when I found PageFour, courtesy of literatureandlatte.com. I’ve only just played around with it a little bit so far and it looks really close to what I’ve wanted, for like, forever. Clean, simple, organized. Get a free demo on the PageFour download.

Oh the humanity

Picked up my copy of Second Person today. It looks really good!

And to top it all off, I got the Ubuntu book Ubuntu Linux for non-geeks. Maybe I’ll print myself a ‘non-geek’ sticker. The upshot of the story is that I’m writing this post from my new Ubuntu gnome desktop! Ubuntu 6.06 installed flawlessly on my (1999) 600x. Everything works. My wireless card even works better than it did under Windows 2000. Go figure.

Anyway, I’m not planning at the moment to use this system for IF, but I do have a notion to get a mush server running….the thrills never stop.

Follow

Get every new post delivered to your Inbox.