class UserData(db.Model): user = db.UserProperty(required=True, auto_current_user_add=True) url = db.LinkProperty(required=True) ref = db.ReferenceProperty() date = db.DateTimeProperty(auto_now_add=True)
class BusStop(db.Model): stopId = db.StringProperty() stopPosition = db.ListProperty(str) stopDesc = db.StringProperty() reqDate = db.DateTimeProperty(auto_now_add=True) reqUser = db.UserProperty()
class Store(DictModel): name = db.StringProperty() content = db.TextProperty() typeo = db.StringProperty() date = db.DateTimeProperty() owner = db.UserProperty()
class RegisteredUser(db.Model): user = db.UserProperty() registered = db.DateTimeProperty(auto_now_add=True)
class Rate(db.Model): author = db.UserProperty() riff = db.ReferenceProperty()
class GAESubscription(db.Model): user = db.UserProperty() feed = db.ReferenceProperty(GAEFeed) up = db.IntegerProperty() down = db.IntegerProperty()
class UserSettings(db.Model): user = db.UserProperty(required=True) seen_example = db.BooleanProperty(default=False) seen_guiders = db.StringListProperty() # the last version (list of ints) this person has viewed the release notes for seen_version = db.ListProperty(int, default=None) locale = db.StringProperty(default=None) chimped = db.BooleanProperty(default=False) @staticmethod def has_seen_example(): user = gae_users.get_current_user() if not user or not user.user_id(): raise Exception("Logged in user expected") settings = UserSettings.get_or_insert(user.user_id(), user=user) return settings.seen_example @staticmethod def mark_example_as_seen(): user = gae_users.get_current_user() if not user or not user.user_id(): raise Exception("Logged in user expected") settings = UserSettings.get_or_insert(user.user_id(), user=user) settings.seen_example = True settings.put() @staticmethod def show_guider(guider_name): user = gae_users.get_current_user() if not user or not user.user_id(): return False settings = UserSettings.get_or_insert(user.user_id(), user=user) return (guider_name not in settings.seen_guiders) @staticmethod def mark_guider_as_seen(guider_name): user = gae_users.get_current_user() if not user or not user.user_id(): return settings = UserSettings.get_or_insert(user.user_id(), user=user) if not guider_name in settings.seen_guiders: settings.seen_guiders.append(guider_name) settings.put() @staticmethod def has_seen_version(version): user = gae_users.get_current_user() if not user or not user.user_id(): return True # don't bother displaying "new version available" to non-authenticated users settings = UserSettings.get_or_insert(user.user_id(), user=user) if not settings.seen_version: settings.seen_version = [0, 0, 0] settings.put() return settings.seen_version >= version @staticmethod def mark_version_as_seen(version): user = gae_users.get_current_user() if not user or not user.user_id(): return settings = UserSettings.get_or_insert(user.user_id(), user=user) settings.seen_version = version settings.put() @staticmethod def get_locale(): user = gae_users.get_current_user() if not user or not user.user_id(): return None settings = UserSettings.get_or_insert(user.user_id(), user=user) return settings.locale @staticmethod def set_locale(locale): user = gae_users.get_current_user() if not user or not user.user_id(): return settings = UserSettings.get_or_insert(user.user_id(), user=user) settings.locale = locale settings.put()
class User(db.Model): user = db.UserProperty(required=False) dispname = db.StringProperty() email = db.StringProperty() isadmin = db.BooleanProperty(default=False)
class Board(db.Model): dateTimeCreated = db.DateTimeProperty() dateTimeStarted = db.DateTimeProperty() dateTimeEnded = db.DateTimeProperty() gameKey = db.StringProperty() resources = db.StringListProperty() hexProduces = db.StringListProperty( ) # must be same dimension as resources playerColors = db.StringListProperty() dice = db.ListProperty(int) diceValues = db.ListProperty(int) owner = db.UserProperty() gamePhase = db.IntegerProperty() currentPlayerRef = db.IntegerProperty() turnPhase = db.IntegerProperty() playOrder = db.ListProperty(int) winner = db.UserProperty() minimumPlayers = db.IntegerProperty() resourceMap = None def save(self, callback): rpc = db.create_rpc(deadline=5, callback=callback) self.put(rpc) def getGamePhases(self): return db.Query(GamePhase).ancestor(self).order("order").fetch(100) def getGamePhase(self, order): return db.Query(GamePhase).ancestor(self).filter("order =", order).get() def getGamePhaseByName(self, phase): return db.Query(GamePhase).ancestor(self).filter("phase =", phase).get() def getCurrentGamePhase(self): if self.gamePhase is None: return None else: return self.getGamePhase(self.gamePhase) def getResourceByHexType(self, hexType): if self.resourceMap is None: self.resourceMap = dict() for i in range(len(self.hexProduces)): #TODO: error check this self.resourceMap[self.hexProduces[i]] = self.resources[i] return self.resourceMap.get(hexType, None) def getCurrentTurnPhase(self): if self.turnPhase is None: return None gp = self.getCurrentGamePhase() if gp is None: return None return db.Query(TurnPhase).ancestor(gp).filter("order =", self.turnPhase).get() def getCurrentPlayerColor(self): p = self.getCurrentPlayer() if p is None: return None return p.color def moveNextPlayer(self): players = self.getPlayers() self.currentPlayerRef += 1 if self.currentPlayerRef >= len(players): self.currentPlayerRef = self.currentPlayerRef % len(players) self.put() def movePrevPlayer(self): players = self.getPlayers() self.currentPlayerRef -= 1 if self.currentPlayerRef < 0: self.currentPlayerRef = self.currentPlayerRef % len(players) self.put() def getCurrentPlayer(self): if self.currentPlayerRef is None: return None else: return db.Query(Player).ancestor(self).filter( "order =", self.currentPlayerRef).get() #TODO: add all deck of development cards def getVertexes(self): return db.Query(Vertex).ancestor(self).fetch(1000) def getEdges(self): return db.Query(Edge).ancestor(self).fetch(1000) def getHexes(self): return db.Query(Hex).ancestor(self).fetch(1000) def getHexesByValue(self, value): return db.Query(Hex).ancestor(self).filter("value =", value).fetch(1000) def getVertex(self, x, y): return db.Query(Vertex).ancestor(self).filter('x =', x).filter('y =', y).get() def getEdge(self, x1, y1, x2, y2): return db.Query(Edge).ancestor(self).filter('x1 =', x1).filter( 'y1 =', y1).filter('x2 =', x2).filter('y2 =', y2).get() def getHex(self, x, y): return db.Query(Hex).ancestor(self).filter('x =', x).filter('y =', y).get() def addPlayer(self, color, user): p = Player(parent=self, color=color, user=user) p.put() logging.info("player added: %s" % (user, )) return p def addReservation(self, reservationKey, reservedFor, expirationDateTime): r = Reservation(parent=self, reservationKey=reservationKey, reservedFor=reservedFor, expirationDateTime=expirationDateTime) r.put() logging.info("reserved for: %s" % (reservedFor, )) return r def getPlayer(self, user): return db.Query(Player).ancestor(self).filter('user =', user).get() def getReservations(self): return db.Query(Reservation).ancestor(self).filter( "expirationDateTime <", datetime.datetime.now()).fetch(100) def getReservationCount(self): return db.Query(Reservation).ancestor(self).filter( "expirationDateTime <", datetime.datetime.now()).count(100) def getReservation(self, reservationKey): return db.Query(Reservation).ancestor(self).filter( "reservationKey =", reservationKey).get() def getReservationByUser(self, user): return db.Query(Reservation).ancestor(self).filter( "reservedFor = ", user).get() def getPlayers(self): return db.Query(Player).ancestor(self).fetch(1000) def getPlayerColorMap(self): ret = dict() players = self.getPlayers() for p in players: ret[p.color] = p return ret def getDevelopmentTypeCost(self, name): ret = dict() dt = db.Query(DevelopmentType).ancestor(self).filter("name =", name).get() if dt is None: return ret return dt.getCost() def getDevelopmentTypeMapByLocation(self, location): ret = dict() devTypes = db.Query(DevelopmentType).ancestor(self).filter( "location =", location).fetch(100) for dt in devTypes: ret[dt.name] = dt cost = db.Query(DevelopmentTypeCost).ancestor(dt).fetch(100) ret["cost"] = dict() for c in cost: ret["cost"][c.resource] = c.amount return ret def dump(self, fp): json.dump(self, fp, cls=BoardEncoder)
class Feedback(db.Model): user = db.UserProperty(auto_current_user_add=True) event = db.ReferenceProperty(Event) rating = db.IntegerProperty() comment = db.StringProperty(multiline=True) created = db.DateTimeProperty(auto_now_add=True)
class Rsvp(db.Model): user = db.UserProperty(auto_current_user_add=True) event = db.ReferenceProperty(Event, collection_name='rsvps') created = db.DateTimeProperty(auto_now_add=True)
class Event(db.Model): status = db.StringProperty(required=True, default='pending', choices=set([ 'pending', 'understaffed', 'approved', 'not_approved', 'canceled', 'onhold', 'expired', 'deleted' ])) # If the member who created the event is now suspended, what the previous # event status was. original_status = db.StringProperty(default=None, choices=set([ 'pending', 'understaffed', 'approved', 'not_approved', 'canceled', 'onhold', 'expired', 'deleted' ])) member = db.UserProperty(auto_current_user_add=True) name = db.StringProperty(required=True) start_time = db.DateTimeProperty(required=True) end_time = db.DateTimeProperty() staff = db.ListProperty(users.User) rooms = db.StringListProperty() #choices=set(ROOM_OPTIONS) details = db.TextProperty(required=True) admin_notes = db.TextProperty(default="") url = db.StringProperty(default="") fee = db.StringProperty(default="") notes = db.TextProperty(default="") type = db.StringProperty(required=True) estimated_size = db.StringProperty(required=True) reminded = db.BooleanProperty(default=False) contact_name = db.StringProperty(default="") contact_phone = db.StringProperty(default="") expired = db.DateTimeProperty() created = db.DateTimeProperty(auto_now_add=True) updated = db.DateTimeProperty(auto_now=True) # Teardown / setup to avoid double-bookings setup = db.IntegerProperty() teardown = db.IntegerProperty() # An alternate person that will be responsible for the event, that must be # specified for events 24 hours or longer. other_member = db.StringProperty(default="") # When the member who owns this event was suspended, if they are. owner_suspended_time = db.DateTimeProperty() @classmethod def check_conflict(cls, proposed_start_time, proposed_end_time, setup, teardown, proposed_rooms, optional_existing_event_id=0): # Figure out how long we need to pad the start and end times of the event. # This is more complicated that it seems, because setup and teardown can # overlap, but there still must be a minimum amount of time between # consecutive events. conf = Config() start_padding = max(int(setup), conf.MIN_EVENT_SPACING) end_padding = max(int(teardown), conf.MIN_EVENT_SPACING) proposed_start_time -= timedelta(minutes=start_padding) proposed_end_time += timedelta(minutes=end_padding) possible_conflicts = cls.all() \ .filter('end_time >', proposed_start_time) \ .filter('status IN', ['approved', 'pending', 'onhold']) conflicts = [] for e in possible_conflicts: if e.key().id() != optional_existing_event_id: if e.start_time < proposed_end_time: for r in e.rooms: if r in proposed_rooms: if e not in conflicts: conflicts.append(e) return conflicts @classmethod def get_all_future_list(cls): return cls.all() \ .filter('start_time >', local_today()) \ .filter('status IN', ['approved', 'not_approved', 'canceled', 'pending', 'onhold']) \ .order('start_time') @classmethod def get_large_list(cls): future_list = Event.get_approved_list() large_list = [] for e in future_list: if int(e.estimated_size) >= 50: large_list.append(e) return large_list @classmethod def get_approved_list(cls): return cls.all() \ .filter('start_time >', local_today()) \ .filter('status IN', ['approved', 'canceled']) \ .order('start_time') @classmethod def get_approved_list_with_multiday(cls): events = list(cls.all() \ .filter('end_time >', local_today()) \ .filter('status IN', ['approved', 'canceled'])) # create dupe event objects for each day of multiday events for event in list(events): if event.start_time < local_today(): # remove original if it started before today events.remove(event) for day in range(1, event.num_days): if event.start_time + timedelta(days=day) >= local_today(): clone = copy(event) clone.start_time = datetime.combine( event.start_date(), time()) + timedelta(days=day) clone.is_continued = True events.append(clone) events.sort(key=lambda event: event.start_time) return events @classmethod def get_recent_past_and_future(cls): return cls.all() \ .filter('start_time >', local_today() - timedelta(days=1)) \ .filter('status IN', ['approved', 'canceled']) \ .order('start_time').fetch(200) @classmethod def get_recent_past_and_future_approved(cls): return cls.all() \ .filter('start_time >', local_today() - timedelta(days=1)) \ .filter('status IN', ['approved']) \ .order('start_time').fetch(200) @classmethod def get_pending_list(cls): return cls.all() \ .filter('start_time >', local_today()) \ .filter('status IN', ['pending', 'understaffed', 'onhold', 'expired']) \ .order('start_time') @classmethod # show last 60 days and all future not approved events def get_recent_not_approved_list(cls): return cls.all() \ .filter('start_time >', local_today() - timedelta(days=60)) \ .filter('status IN', ['not_approved']) \ .order('start_time') def owner(self): return human_username(self.member) def stafflist(self): return to_sentence_list(map(human_username, self.staff)) def roomlist(self): return to_sentence_list(self.rooms) def roomlist_as_phrase(self): if len(self.rooms) > 0: return "in " + self.roomlist() else: return "" def is_staffed(self): return len(self.staff) >= self.staff_needed() def staff_needed(self): return 0 # if self.estimated_size.isdigit(): # return int(self.estimated_size) / GUESTS_PER_STAFF # else: # # invalid data; just return something reasonable # return 2 def is_approved(self): """Has the events team approved the event? Note: This does not necessarily imply that the event is in state 'approved'.""" return self.status in ('understaffed', 'approved', 'cancelled') def is_canceled(self): return self.status == 'canceled' def is_onhold(self): return self.status == 'onhold' def is_deleted(self): return self.status == 'deleted' def is_past(self): return self.end_time < local_today() def is_not_approved(self): return self.status == 'not_approved' def start_date(self): return self.start_time.date() def end_date(self): return self.end_time.date() @property def num_days(self): num_days = (self.end_date() - self.start_date()).days + 1 if num_days > 1 and self.end_time.timetuple()[3] < 8: # only count that day if the event runs past 8am num_days -= 1 return num_days def multiday(self): self.num_days > 1 def approve(self): user = users.get_current_user() if self.is_staffed(): self.expired = None self.status = 'approved' logging.info('%s approved %s' % (user.nickname(), self.name)) else: self.status = 'understaffed' logging.info('%s approved %s but it is still understaffed' % (user.nickname, self.name)) self.put() def rsvp(self): user = users.get_current_user() if user and not self.has_rsvped(): rsvp = Rsvp(event=self) rsvp.put() def has_rsvped(self): user = users.get_current_user() if not user: return False for existing_rsvp in self.rsvps: if existing_rsvp.user == user: return True return False # Works even for logged out users def can_rsvp(self): if self.has_rsvped(): return False time_till_event = self.start_time.replace( tzinfo=pytz.timezone('US/Pacific')) - datetime.now( pytz.timezone('US/Pacific')) hours = time_till_event.seconds / 3600 + time_till_event.days * 24 return (hours > RSVP_DEADLINE) def cancel(self): user = users.get_current_user() self.status = 'canceled' self.put() logging.info('%s canceled %s' % (user.nickname(), self.name)) def on_hold(self): user = users.get_current_user() self.status = 'onhold' self.put() logging.info('%s put %s on hold' % (user.nickname(), self.name)) def not_approved(self): user = users.get_current_user() self.status = 'not_approved' self.put() logging.info('%s not_approved %s' % (user.nickname(), self.name)) def delete(self): user = users.get_current_user() self.status = 'deleted' self.put() logging.info('%s deleted %s' % (user.nickname(), self.name)) def undelete(self): user = users.get_current_user() self.status = 'pending' self.put() logging.info('%s undeleted %s' % (user.nickname(), self.name)) def delete(self): user = users.get_current_user() self.status = 'deleted' self.put() logging.info('%s deleted %s' % (user.nickname(), self.name)) def undelete(self): user = users.get_current_user() self.status = 'pending' self.put() logging.info('%s undeleted %s' % (user.nickname(), self.name)) def expire(self): user = users.get_current_user() self.expired = datetime.now() self.status = 'expired' self.put() logging.info('%s expired %s' % (user.nickname(), self.name)) def add_staff(self, user): self.staff.append(user) if self.is_staffed() and self.status == 'understaffed': self.status = 'approved' self.put() logging.info('%s staffed %s' % (user.nickname(), self.name)) def remove_staff(self, user): self.staff.remove(user) if not self.is_staffed() and self.status == 'approved': self.status = 'understaffed' self.put() logging.info('%s staffed %s' % (user.nickname(), self.name)) def to_dict(self, summarize=False): d = dict() if summarize: props = [ 'member', 'start_time', 'name', 'type', 'estimated_size', 'end_time', 'rooms', 'status' ] else: props = Event.properties().keys() for prop in props: if prop == 'member': d[prop] = getattr(self, prop).email() elif prop == 'staff': d[prop] = map(lambda x: x.email(), getattr(self, prop)) elif prop in [ 'start_time', 'end_time', 'created', 'expired', 'updated' ]: if getattr(self, prop): d[prop] = getattr(self, prop).replace(tzinfo=pytz.timezone( 'US/Pacific')).strftime('%Y-%m-%dT%H:%M:%S') else: d[prop] = getattr(self, prop) d['id'] = self.key().id() return d def human_time(self): start = self.start_time.strftime("%m/%d/%y %I:%M%p") if self.multiday(): end = self.end_time.strftime("%m/%d/%y %I:%M%p") else: end = self.end_time.strftime("%I:%M%p") out = "%s to %s" % (start, end) if self.multiday(): out += " (multiday)" return out def full_url(self): protocol = re.compile("^https?:\/\/") if protocol.search(self.url): return self.url return "http://" + self.url
class UserUpload(db.Model): user = db.UserProperty() description = db.StringProperty() blob = blobstore.BlobReferenceProperty()
class Communication(db.Model): author = db.UserProperty() receiver = db.UserProperty() content = db.StringProperty(multiline = True) date = db.DateTimeProperty(auto_now_add = True)
class Group(db.Model): MapId = db.IntegerProperty(required=False) Title = db.StringProperty(required=True) Description = db.StringProperty(required=False) Owner = db.UserProperty(required=True)
class Reservation(db.Model): reservationKey = db.StringProperty() reservedFor = db.UserProperty() expirationDateTime = db.DateTimeProperty()
class GAEUser(db.Model): user = db.UserProperty() lastupdate = db.DateTimeProperty() worddb = db.BlobProperty()
class Player(db.Model): color = db.StringProperty() user = db.UserProperty() score = db.IntegerProperty(default=0) order = db.IntegerProperty() # send a dict of resources, integer mappings to add to players resources # negative integers subtract resources # if a resource isn't listed under a player it is assumed they have zero def resetResources(self): resource_dict = dict() for r in self.parent().resources: resource_dict[r] = 0 #TODO: add exception handling db.run_in_transaction(self.__setResourcesTran, resource_dict) def __setResourcesTran(self, resource_dict): rd = resource_dict.copy() #HACK: shouldn't be more than 25 resource types, but still... playerResources = db.Query(PlayerResources).ancestor(self).fetch(25) #TODO: add transactions around this logic # first add to the resources we know about for pr in playerResources: if not rd.get(pr.resource, None) is None: pr.amount = rd[pr.resource] pr.put() del rd[pr.resource] # then loop through remaining resources and add them as player resources for r, a in rd.items(): pr = PlayerResources(parent=self, resource=r, amount=a) pr.put() return True def adjustResources(self, resource_dict, validate_only=False): ret = db.run_in_transaction(self.__adjustResourcesTrans, resource_dict, validate_only) if ret is None: return False else: return ret def __adjustResourcesTrans(self, resource_dict, validate_only): rd = resource_dict.copy() #HACK: shouldn't be more than 25 resource types, but still... playerResources = db.Query(PlayerResources).ancestor(self).fetch(25) #TODO: add transactions around this logic # first add to the resources we know about for pr in playerResources: if not rd.get(pr.resource, None) is None: if pr.amount + rd[pr.resource] < 0: raise db.Rollback() elif not validate_only: pr.amount += rd[pr.resource] pr.put() del rd[pr.resource] # then loop through remaining resources and add them as player resources for r, a in rd.items(): if a < 0: raise db.Rollback() elif not validate_only: logging.info("adjusting %s = %d:" % (r, a)) pr = PlayerResources(parent=self, resource=r, amount=a) pr.put() return True
class GAESeenPost(db.Model): user = db.UserProperty() feed = db.ReferenceProperty(GAEFeed) # non-normalized post = db.ReferenceProperty(GAEPost)
class UserStat(db.Model): pic = db.ReferenceProperty(TexRender) user = db.UserProperty() # TODO: add user commenting capabilities
class Greeting(db.Model): """Models an individual Guestbook entry with an author, content, and date.""" author = db.UserProperty() content = db.StringProperty(multiline=True) date = db.DateTimeProperty(auto_now_add=True)
class UserData(db.Model): user = db.UserProperty() aws_username = db.StringProperty()
class RegisteredUserSettings(db.Model): user = db.UserProperty() last_changed = db.DateTimeProperty(auto_now=True) paging_pager_count = db.IntegerProperty(required=True)
class Post(db.Model): title = db.StringProperty() slug = db.StringProperty() pub_date = db.DateTimeProperty(auto_now_add=True) author = db.UserProperty(auto_current_user_add=True) excerpt = db.TextProperty(default=None) body = db.TextProperty() excerpt_html = db.TextProperty(default=None) body_html = db.TextProperty() tags = db.StringListProperty() def get_absolute_url(self): return "/blog/%04d/%02d/%02d/%s" % (self.pub_date.year, self.pub_date.month, self.pub_date.day, self.slug) def get_edit_url(self): return "/admin/post/edit/%04d/%02d/%02d/%s" % ( self.pub_date.year, self.pub_date.month, self.pub_date.day, self.slug) def get_delete_url(self): return "/admin/post/delete/%04d/%02d/%02d/%s" % ( self.pub_date.year, self.pub_date.month, self.pub_date.day, self.slug) def delete(self): if db.delete(self): return 1 else: return 0 def put(self): """ Make sure that the slug is unique for the given date before the data is actually saved. """ # Delete the cached archive list if we are saving a new post if not self.is_saved(): memcache.delete('archive_list') # Delete the cached tag list whenever a post is created/updated memcache.delete('tag_list') self.test_for_slug_collision() self.populate_html_fields() key = super(Post, self).put() return key def test_for_slug_collision(self): # Build the time span to check for slug uniqueness start_date = datetime.datetime(self.pub_date.year, self.pub_date.month, self.pub_date.day) time_delta = datetime.timedelta(days=1) end_date = start_date + time_delta # Create a query to check for slug uniqueness in the specified time span query = Post.all(keys_only=True) query.filter('pub_date >= ', start_date) query.filter('pub_date < ', end_date) query.filter('slug = ', self.slug) # Get the Post Key that match the given query (if it exists) post = query.get() # If any slug matches were found then an exception should be raised if post and (not self.is_saved() or self.key() != post): raise SlugConstraintViolation(start_date, self.slug) def populate_html_fields(self): # Setup Markdown with the code highlighter md = markdown.Markdown(extensions=['codehilite']) # Convert the excerpt and body Markdown into html if self.excerpt != None: self.excerpt_html = md.convert(self.excerpt) if self.body != None: self.body_html = md.convert(self.body)
class FileMetadata(db.Model): """A helper class that will hold metadata for the user's blobs. Specifially, we want to keep track of who uploaded it, where they uploaded it from (right now they can only upload from their computer, but in the future urlfetch would be nice to add), and links to the results of their MR jobs. To enable our querying to scan over our input data, we store keys in the form 'user/date/blob_key', where 'user' is the given user's e-mail address, 'date' is the date and time that they uploaded the item on, and 'blob_key' indicates the location in the Blobstore that the item can be found at. '/' is not the actual separator between these values - we use '..' since it is an illegal set of characters for an e-mail address to contain. """ __SEP = ".." __NEXT = "./" owner = db.UserProperty() filename = db.StringProperty() uploadedOn = db.DateTimeProperty() source = db.StringProperty() blobkey = db.StringProperty() song_sales_link = db.StringListProperty() song_profit_link = db.StringListProperty() artist_songs_link = db.StringListProperty() artist_profit_link = db.StringListProperty() genre_song_sales_link = db.StringListProperty() genre_song_profit_link = db.StringListProperty() genre_artist_songs_link = db.StringListProperty() genre_artist_profit_link = db.StringListProperty() find_most_common_link = db.StringListProperty() @staticmethod def getFirstKeyForUser(username): """Helper function that returns the first possible key a user could own. This is useful for table scanning, in conjunction with getLastKeyForUser. Args: username: The given user's e-mail address. Returns: The internal key representing the earliest possible key that a user could own (although the value of this key is not able to be used for actual user data). """ return db.Key.from_path("FileMetadata", username + FileMetadata.__SEP) @staticmethod def getLastKeyForUser(username): """Helper function that returns the last possible key a user could own. This is useful for table scanning, in conjunction with getFirstKeyForUser. Args: username: The given user's e-mail address. Returns: The internal key representing the last possible key that a user could own (although the value of this key is not able to be used for actual user data). """ return db.Key.from_path("FileMetadata", username + FileMetadata.__NEXT) @staticmethod def getKeyName(username, date, blob_key): """Returns the internal key for a particular item in the database. Our items are stored with keys of the form 'user/date/blob_key' ('/' is not the real separator, but __SEP is). Args: username: The given user's e-mail address. date: A datetime object representing the date and time that an input file was uploaded to this app. blob_key: The blob key corresponding to the location of the input file in the Blobstore. Returns: The internal key for the item specified by (username, date, blob_key). """ sep = FileMetadata.__SEP return str(username + sep + str(date) + sep + blob_key)
class Account(db.Model): user = db.UserProperty()
class User(BaseModel): """A model with the same attributes and methods as a Django user model. The model has two additions. The first addition is a 'user' attribute which references a App Engine user. The second is the 'get_djangouser_for_user' classmethod that should be used to retrieve a DjangoUser instance from a App Engine user object. """ user = db.UserProperty(required=True) username = db.StringProperty(required=True) first_name = db.StringProperty() last_name = db.StringProperty() email = db.EmailProperty() password = db.StringProperty() is_staff = db.BooleanProperty(default=False, required=True) is_active = db.BooleanProperty(default=True, required=True) is_superuser = db.BooleanProperty(default=False, required=True) last_login = db.DateTimeProperty(auto_now_add=True, required=True) date_joined = db.DateTimeProperty(auto_now_add=True, required=True) groups = EmptyManager() user_permissions = EmptyManager() def __unicode__(self): return self.username def __str__(self): return unicode(self).encode('utf-8') @classmethod def get_djangouser_for_user(cls, user): query = cls.all().filter("user ="******"""Gets and deletes messages for this user""" msgs = [] for msg in self.message_set: msgs.append(msg) msg.delete() return msgs def is_anonymous(self): """Always return False""" return False def is_authenticated(self): """Always return True""" return True def get_absolute_url(self): return "/users/%s/" % urllib.quote(smart_str(self.username)) def get_full_name(self): full_name = u'%s %s' % (self.first_name, self.last_name) return full_name.strip() def email_user(self, subject, message, from_email): """Sends an email to this user. According to the App Engine email API the from_email must be the email address of a registered administrator for the application. """ mail.send_mail(subject, message, from_email, [self.email]) @property def pk(self): try: return str(self.key()) except db.NotSavedError: pass def get_profile(self): """ Returns site-specific profile for this user. Raises SiteProfileNotAvailable if this site does not allow profiles. When using the App Engine authentication framework, users are created automatically. """ from django.contrib.auth.models import SiteProfileNotAvailable if not hasattr(self, '_profile_cache'): from django.conf import settings if not hasattr(settings, "AUTH_PROFILE_MODULE"): raise SiteProfileNotAvailable try: app_label, model_name = settings.AUTH_PROFILE_MODULE.split('.') model = models.get_model(app_label, model_name) self._profile_cache = model.all().filter("user ="******"id"): created = True super(User, self).save() if created: user_created.send(sender=self.__class__, instance=self)
class GeoLink(db.Model): url = db.StringProperty() user = db.UserProperty() shortlink = db.StringProperty() linkmap = db.TextProperty() linkmap_key = db.StringProperty() created_on = db.DateTimeProperty(auto_now_add=True) @property def geolink(self): if self.shortlink is None: return GEOLINK_BASE_URL + 'l/' + str(self.key()) else: return self.shortlink @property def infolink(self): return GEOLINK_BASE_URL + 'i/' + str(self.key()) def linkmap_find(self, code_value): value = None lines = self.linkmap.split('\n') for line in lines: parts = line.split('|') if parts[0].rstrip().lower() == code_value.lower(): value = parts[1].rstrip() if parts[1].rstrip().lower() == code_value.lower(): value = parts[0].rstrip() return value def validate(self): isValidInput = False if self.url: #validate input url link_code_count = 0 for code in GEOLINK_CODES: link_code_start = self.url.find(code) if link_code_start != -1: link_code_count += 1 if link_code_count == 0: raise main.ValidationError("link must use at least 1 code") else: if GeoLink.validateURL(self.url): isValidInput = True elif self.linkmap: #validate input linkmap and linkmap_key if self.linkmap and self.linkmap_key: valid_codes = None if self.linkmap_key == "state_code": valid_codes = LIST_STATE_CODES elif self.linkmap_key == "state": valid_codes = LIST_STATES else: raise main.ValidationError("Invalid code for map") lines = self.linkmap.split('\n') for line in lines: parts = line.split('|') if len(parts) != 2: raise main.ValidationError( "Each line must have a code and a URL seperated by |" ) if parts[0].rstrip().upper() in valid_codes: GeoLink.validateURL(parts[1]) elif parts[1].rstrip().upper() in valid_codes: GeoLink.validateURL(parts[0]) else: raise main.ValidationError( "Missing or invalid code value") isValidInput = True else: raise main.ValidationError("Must enter link or linkmap") return isValidInput @classmethod def validateURL(cls, url): #TODO: throw exception instead of catching parts = None try: parts = urlparse.urlparse(url) except Exception, e: logging.error(e) raise main.ValidationError("URL is not valid") if not all([parts.scheme, parts.netloc]): raise main.ValidationError("URL not complete") if parts.scheme not in ['http', 'https']: raise main.ValidationError("URL missing scheme (http or https)") return True
class Greeting(db.Model): author = db.UserProperty() content = db.StringProperty(multiline=True) date = db.DateTimeProperty(auto_now_add=True)
class Feedback(db.Model): author = db.UserProperty() author_nickname = db.StringProperty() content = db.TextProperty() date = db.DateTimeProperty(auto_now_add=True) deleted = db.BooleanProperty(default=False) targets = db.ListProperty(db.Key) # first element is video key. # optional second element is question key. types = db.StringListProperty() is_flagged = db.BooleanProperty(default=False) is_hidden_by_flags = db.BooleanProperty(default=False) flags = db.StringListProperty(default=None) flagged_by = db.StringListProperty(default=None) sum_votes = db.IntegerProperty(default=0) inner_score = db.FloatProperty(default=0.0) @staticmethod def cache_key_for_video(video): return "videofeedbackcache:%s" % video.key() def __init__(self, *args, **kwargs): db.Model.__init__(self, *args, **kwargs) self.children_cache = [ ] # For caching each question's answers during render def clear_cache_for_video(self): layer_cache.BlobCache.delete(Feedback.cache_key_for_video( self.video()), namespace=App.version) def delete(self): db.delete(self) self.clear_cache_for_video() def put(self): db.Model.put(self) self.clear_cache_for_video() def set_author(self, user_data): self.author = user_data.user self.author_nickname = user_data.nickname def authored_by(self, user_data): return user_data and self.author == user_data.user def is_visible_to_public(self): return (not self.deleted and not self.is_hidden_by_flags) def is_visible_to(self, user_data): """ Returns true if this post should be visible to user_data. If user_data is empty, true only if the post should be visible to the general public. If someone's post has been deleted or flagged, it's only visible to the original author and developers. """ return self.is_visible_to_public() or self.authored_by(user_data) or ( user_data and user_data.developer) def appears_as_deleted_to(self, user_data): """ Returns true if the post should appear as deleted to user_data. This should only be true for posts that are deleted and being viewed by developers. """ return user_data and (user_data.developer or user_data.moderator ) and not self.is_visible_to_public() @property def sum_votes_incremented(self): # Always add an extra vote when displaying vote counts to convey the author's implicit "vote" # and make the site a little more positive. return self.sum_votes + 1 def is_type(self, type): return type in self.types def question_key(self): if self.targets: return self.targets[-1] # last target is always the question return None def question(self): return db.get(self.question_key()) def children_keys(self): keys = db.Query(Feedback, keys_only=True) keys.filter("targets = ", self.key()) return keys def video_key(self): if self.targets: return self.targets[0] return None def video(self): video_key = self.video_key() if video_key: video = db.get(video_key) if video and video.has_topic(): return video return None def add_vote_by(self, vote_type, user_data): FeedbackVote.add_vote(self, vote_type, user_data) self.update_votes_and_score() def update_votes_and_score(self): self.recalculate_votes() self.recalculate_score() self.put() if self.is_type(FeedbackType.Answer): question = self.question() question.recalculate_score() question.put() def recalculate_votes(self): self.sum_votes = FeedbackVote.count_votes(self) def recalculate_score(self): score = float(self.sum_votes) if self.is_type(FeedbackType.Question): for answer in db.get(self.children_keys().fetch(1000)): score += 0.5 * float(answer.sum_votes) self.inner_score = float(score) def add_flag_by(self, flag_type, user_data): if user_data.key_email in self.flagged_by: return False self.flags.append(flag_type) self.flagged_by.append(user_data.key_email) self.recalculate_flagged() return True def clear_flags(self): self.flags = [] self.flagged_by = [] self.recalculate_flagged() def recalculate_flagged(self): self.is_flagged = len(self.flags or []) > 0 self.is_hidden_by_flags = len(self.flags or []) >= FeedbackFlag.HIDE_LIMIT