import cgi, os, re, sys, urllib, hashlib from datetime import datetime from google.appengine.api import users from google.appengine.ext import db def insert_or_update_(model, key_name, **kw): review = model.get_by_key_name(key_name) if not review: review = model(key_name=key_name, **kw) else: keys = kw.keys() for key in keys: setattr(review, key, kw[key]) review.put() return review def insert_or_update(cls, key_name, **kw): return db.run_in_transaction(insert_or_update_, cls, key_name, **kw) def get_by_primary_key(cls, *args): return cls.get_by_key_name(cls.primary_key(*args)) def goi_by_primary_key(cls, *args, **kw): return cls.get_or_insert(cls.primary_key(*args), **kw) def iou_by_primary_key(cls, *args, **kw): return cls.insert_or_update(cls.primary_key(*args), **kw) def saurik(main): db.Model.insert_or_update = classmethod(insert_or_update) db.Model.get_by_primary_key = classmethod(get_by_primary_key) db.Model.goi_by_primary_key = classmethod(goi_by_primary_key) db.Model.iou_by_primary_key = classmethod(iou_by_primary_key) main() def head(title, scripts, styles): print 'Content-Type: text/html' print 'Cache-Control: private, proxy-revalidate' print '' print '' print '' if title: print '%s' % title for script in scripts: print '' % script for style in styles: print '' % style print '' def foot(): print '' def debug(): for key in os.environ.keys(): print key, ' = ', os.environ[key] def auth(): user = users.get_current_user() if not user: #host = os.environ['HTTP_HOST'] path = os.environ['PATH_INFO'] #uri = 'http://' + host + path uri = path print 'Location: ' + users.create_login_url(uri) print '' exit() role = os.environ["HTTP_X_ROLE"] account = GoogleUser.goi_account_by_primary_key(user, role=role) agent = os.environ["HTTP_USER_AGENT"] match = re.search('.*iPhone OS ([0-9_]*).*', agent) firmware = match.group(1).replace('_', '.') if match else None device_id = os.environ['HTTP_X_UNIQUE_ID'] machine = os.environ["HTTP_X_MACHINE"] device = Device.iou_by_primary_key(device_id, id=device_id, firmware=firmware, machine=machine, account=account) return user, account, device class FiveStarRatingProperty(db.IntegerProperty): def __init__(self, **kw): super(FiveStarRatingProperty, self).__init__(choices=set([0, 1, 2, 3, 4, 5]), **kw) class IPAddressProperty(db.StringProperty): def validate(self, value): if not super(IPAddressProperty, self).validate(value): return False # XXX: implement IP address validation return True class Account(db.Model): # control added = db.DateTimeProperty(required=True, auto_now_add=True) index = db.IntegerProperty(required=True, default=0) # implicit role = db.StringProperty(required=True) # explicit nickname = db.StringProperty(required=False) # automatic reviews_count = db.IntegerProperty(required=True, default=0) @staticmethod def monotonic_(id): account = Account.get_by_id(id) index = account.index account.index = index + 1 account.put() return index def monotonic(self): id = self.key().id() return '0|' + str(datetime.today()) + '|' + str(id) + '|' + str(db.run_in_transaction(Account.monotonic_, id)) class GoogleUser(db.Model): # primary user = db.UserProperty(required=True) # control added = db.DateTimeProperty(required=True, auto_now_add=True) # explicit account = db.ReferenceProperty(Account, required=False) #XXX @staticmethod def primary_key(user): sha1 = hashlib.sha1() sha1.update(user.email()) return 'sha1:' + sha1.hexdigest() @staticmethod def goi_account_by_primary_key_(user, hash, **kw): gooser = GoogleUser.get_by_key_name(hash) if gooser: account = db.get(gooser.account) keys = kw.keys() for key in keys: setattr(account, key, kw[key]) account.put() else: account = Account(**kw) account.put() gooser = GoogleUser(parent=account, key_name=hash, user=user, account=account) gooser.put() return account @staticmethod def goi_account_by_primary_key(user, **kw): hash = GoogleUser.primary_key(user) return db.run_in_transaction(GoogleUser.goi_account_by_primary_key_, user, hash, **kw) class Package(db.Model): # primary id = db.StringProperty(required=True) # control noticed = db.DateTimeProperty(required=True, auto_now_add=True) # automatic reviews_count = db.IntegerProperty(required=True, default=0) reviews_avg_concept = db.FloatProperty(required=False) reviews_avg_reality = db.FloatProperty(required=False) reviews_avg_minimum = db.FloatProperty(required=False) def rating(): return self.reviews_avg_minimum @staticmethod def primary_key(id): return id class Device(db.Model): # primary id = db.StringProperty(required=True) # control noticed = db.DateTimeProperty(required=True, auto_now_add=True) # implicit firmware = db.StringProperty(required=True) machine = db.StringProperty(required=True) # explicit account = db.ReferenceProperty(Account, required=False, collection_name='devices') @staticmethod def primary_key(id): return id class Rating(db.Model): # primary package = db.ReferenceProperty(Package, required=True, collection_name='ratings') shard = db.IntegerProperty(required=True, default=0) # internal index = db.IntegerProperty(required=True, default=0) # automatic reviews_count = db.IntegerProperty(required=True, default=0) reviews_sum_concept = db.IntegerProperty(required=True, default=0) reviews_sum_reality = db.IntegerProperty(required=True, default=0) reviews_sum_minimum = db.IntegerProperty(required=True, default=0) @staticmethod def primary_key(package_id, index): return package_id + '|' + index class Review(db.Model): # primary package = db.ReferenceProperty(Package, required=True, collection_name='reviews') account = db.ReferenceProperty(Account, required=True, collection_name='reviews') # control posted = db.DateTimeProperty(required=True, auto_now_add=True) updated = db.DateTimeProperty(required=True, auto_now=True) rating = db.IntegerProperty(required=True, default=0) monotonic = db.StringProperty(required=True) # implicit device = db.ReferenceProperty(Device, required=True, collection_name='reviews') version = db.StringProperty(required=True) # explicit title = db.StringProperty(required=True) concept = FiveStarRatingProperty(required=True) reality = FiveStarRatingProperty(required=True) comment = db.TextProperty(required=True) # automatic account_role = db.StringProperty(required=True) device_firmware = db.StringProperty(required=True) device_machine = db.StringProperty(required=True) @staticmethod def primary_key(package_id, account): return package_id + '|' + str(account.key().id())