0

I'm new to python and GAE and I've been trying to learn how to work with tasks queues, cron, and datastore for my own project through examples but haven't had much luck at all. I trying to modify the Votelator code from this Google Developers tutorial video on YouTube: http://www.youtube.com/watch?v=AM0ZPO7-lcE which can be seen working here: http://voterlator.appspot.com/

I realized that the code wasn't complete, so I decided to try to figure out what was missing. I made some substitutions (e.g., programming languages are now dairy products). There are a few things that aren't worked out in the tutorial code. For example, the variable LANGUAGES is not specified anywhere. It's not clear to me how the data model pulls the values for languages from the form and uses them as keys.

I can't figure out why it's not working. I left some of the code I tried to make it work in there and commented it out.

Here's the app.yaml:

application: voting
version: 1
runtime: python27
api_version: 1
threadsafe: true

handlers:
- url: /static/css
  static_dir: static/css

- url: /static/css/sunny
  static_dir: static/css/sunny

- url: /static/js
  static_dir: static/js

- url: /cron/tally
  script: main.app

- url: /.*
  script: main.app

Here's the cron.yaml:

cron:
- description: vote tallying worker
  url: /cron/tally
  schedule: every 1 minutes

This is the queue.yaml

total_storage_limit: 120M
queue:
- name: votes
  mode: pull

This is the main.py

import webapp2
from google.appengine.ext import db
from google.appengine.api import taskqueue
import os
import jinja2

jinja_environment = jinja2.Environment(
    loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))

class Tally(db.Model):
    """Counting model"""

    count = db.IntegerProperty(default=0, required=True)
    #key_name = ['Milk', 'Butter', 'Cheese'] #added
    #key_name = db.StringProperty(choices=set(['Milk', 'Butter', 'Cheese'])) #added
    cls = ['Milk', 'Butter', 'Cheese'] #added

    @classmethod
    def increment_by(cls, key_name, count):
        """Increases a tally's count. Should be run in a transaction."One tally for each language"""
        """The key_name is going to be the language name"""
        """There's a convenience method there that actually will update the count"""

        tally = cls.get_by_key_name(key_name)
        if tally is None:
            tally = cls(key_name=key_name)
        tally.count += count
        tally.put()
    #db.run_in_transaction(increment_by, key_name, count) #added
        #db.run_in_transaction(cls, key_name, count) #added

    #db.run_in_transaction(Tally.increment_by, cls, count) #added

class VoteHandler(webapp2.RequestHandler):
    """Handles adding of vote tasks"""
    def get(self):
        """Displays the voting form"""


        tally_query = Tally.all() #added
        #tally_query = Tally.all(key_name)   #added  
        tallies = tally_query.fetch(3) #added

        urla = '_ah/admin/'
        url_admin = 'Admin'

        urlvh = '/'
        url_votehand = 'Votes'

        ugliest = self.request.get('ugliest')

        template_values = {
            'tallies': tallies, #added
            'ugliest': ugliest, #added               

            'url_admin': url_admin,
            'urla': urla,

            'urlvh': urlvh,
            'url_votehand': url_votehand,
        }
        template = jinja_environment.get_template('vote_handler.html')
        self.response.out.write(template.render(template_values))
    def post(self):
        """Adds to the votes queue if ugliest is valid. """

        LANGUAGES = ['Milk', 'Butter', 'Cheese'] #added: This is just a guess

        ugliest = self.request.get('ugliest')
        if ugliest and ugliest in LANGUAGES:  ### No variable specified for LANGUAGES
            q = taskqueue.Queue('votes')
            q.add(taskqueue.Task(payload=ugliest, method='PULL'))

        self.redirect('/')

    #db.run_in_transaction(Tally.increment_by(cls, key_name, count)) #added

class TallyHandler(webapp2.RequestHandler):
    def post(self):
        """Leases vote tasks, accumulates tallies and stores them"""
        q = taskqueue.Queue('votes')
        # Keep leasing tasks in a loop.
        while True:
            tasks = q.lease_tasks(300, 1000)
            if not tasks:
                return
            # accumulate tallies in memory
            tallies = {}
            for t in tasks:
                tallies[t.payload] = tallies.get(t.payload, 0) + 1
            self.store_tallies(tallies)
            q.delete_tasks(tasks)

app = webapp2.WSGIApplication([('/', VoteHandler),
                               ('/cron/tally', TallyHandler)], #called from cron, pulls votes from the queue and updating tallies
                          debug=True)



def main():
    app.run()

if __name__ == "__main__":
    main()

This is the code for the form in the vote_handler.html:

<form action="/" method="post">
<input type="radio" name="ugliest" value="Milk" /> Milk<br>
<input type="radio" name="ugliest" value="Butter" /> Butter<br>
<input type="radio" name="ugliest" value="Cheese" /> Cheese
<br>
<div>
<input type="submit" value="Submit">
</div>
</form>

<div class="commentary">
    {% for tally in tallies %}
      {{ tally.count }}
    {% endfor %}
</div>

I tried to get it to display the results just below the form.

What ends up happening is that the form adds tasks to the queue and there's a Task kind in the Datastore but it doesn't have any entities in it. I have no clue what is wrong.

655321
  • 411
  • 4
  • 26

1 Answers1

0

TallyHandler's 'put' method should be renamed 'get'. (A cron job will invoke a URL, using an HTTP GET request.)

Note that cron tasks are not automatically run in the dev environment, only in production. Use the admin console to manually trigger a cron task.

The store_tallies method needs to be defined. See the original source code at http://google-app-engine-samples.googlecode.com/svn/trunk/voterlator/

BTW, in app.yaml I would recommend restricting access to your cron URL(s) using

login: admin
Eric Willigers
  • 1,710
  • 14
  • 9
  • Thanks!!! I checked out the code. I know it's not supposed to be a comprehensive tutorial but there was **a lot** missing from the video. It's working for the most part now, except for the names they are voting on are not showing up on the form. But I can figure that out. – 655321 Jan 29 '13 at 18:22
  • (**2022 update**): Google Code repo was taken down but was archived to https://github.com/jacoblukose/google-app-engine-samples/tree/master/voterlator – wescpy Jul 11 '22 at 03:04