I hope this doesn't get much bigger ... if so I'll start sending a
pointer to it rather than including the code in the group.
Here is a version with the "outer" transaction check flag, and the
mechanism to take the app offline for normal use or make it readonly.
Obviously you'd have to write the code to set and clear these flags,
to check the statsFlag and to "fix" the statistics entity and set the
statsFlag as you go.
If you do this right (update the statistics before you update the
entity record) it will have transactional integrity.
import wsgiref.handlers
import cgi
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext import db
import os
from google.appengine.ext.webapp import template
from google.appengine.ext import db
class Statistics(db.Model):
entryCount = db.IntegerProperty()
entryMin = db.FloatProperty()
entryMax = db.FloatProperty()
entrySum = db.FloatProperty()
class ApplicationFlags(db.Model):
availableFlag = db.BooleanProperty()
readonlyFlag = db.BoolenProperty()
class Reading(db.Model):
xxx = db.StringProperty()
reading = db.FloatProperty()
yyy = db.DateProperty()
statsFlag = db.BooleanProperty()
def increment_counters(key, val):
s = db.get(key)
s.entryCount += 1
if val < s.entryMin: s.entryMin = val
if val > s.entryMax: s.entryMax = val
s.entrySum += val
s.put()
class EnterData(webapp.RequestHandler):
def post(self):
#
# If you want to be able to make the application readonly, you will
need some code here tu check availableFlag,
# tell the user that the application is not available for writes and
then exit
#
flags = db.GblQuery("SELECT * FROM ApplicationFlags")
f = flags.fetch(1)
if f.availableFlag and not f.readonlyFlag:
r = Reading()
r.yyy = datetime.strptime(cgi.escape(self.request.get('Date')),"%Y-
%m-%d").date()
r.reading = float(cgi.escape(self.request.get('Observation')))
r.xxx = "blah"
r.statsFlag = False
r.put()
rk = r.key()
stats = db.GqlQuery("SELECT * FROM Statistics")
s = stats.get()
try:
db.run_in_transaction(increment_counters, s.key(), r.reading)
except TransactionFailedError:
# Delete the entity that we just put() and tell the user to try again
later
db.delete(rk)
displayError(self, "Update failed")
r.statsFlag = True
r.put()
else:
displayError(self,"Application not available for updates, try again
later")
def get(self):
# Display the most recent data and a form to collect a new reading
flags = db.GblQuery("SELECT * FROM ApplicationFlags")
f = flags.fetch(1)
if f.availableFlag and not f.readonlyFlag:
displayData(self, True)
else:
displayError(self,"Application not available for updates, try again
later")
class ReadData(webapp.RequestHandler):
def get(self):
#
# If you want to be able to take the application completely offline
you will need some code here tu check availableFlag,
# tell the user that the application is not available
#
flags = db.GblQuery("SELECT * FROM ApplicationFlags")
f = flags.fetch(1)
if f.availableFlag:
displayData(self, False)
else:
displayError(self,"Application not available, try again later")
def main():
application = webapp.WSGIApplication(
[('/Rainfall', ReadData),
('/Rainfall/Enter', EnterData)],
debug=True)
wsgiref.handlers.CGIHandler().run(application)
if __name__ == "__main__":
main()