class Subscription(CachingModel): """this represents a topic, usually a stream, that a subscriber (usually an inbox) would like to receive updates to """ topic = models.StringProperty() # ref - the stream being subscribed to subscriber = models.StringProperty() # ref - the subscriber (actor) target = models.StringProperty() # where to dump this state = models.StringProperty() # The status of remote subs, see XEP-0060 # sect 4.2. The 'pending' state is ignored if # the target of the subscription is used. # The design is for performance: on public # entries # the state is ignored and listing the # subscriptions is a single query; for # contacts-only entries the state is used but # it is also kept up-to-date regarding buddy # relationships, so a single query for # state='subscribed' can again be used. extra = properties.DictProperty() # holds a bunch of stuff created_at = properties.DateTimeProperty(auto_now_add=True) # for ordering someday key_template = '%(topic)s/%(target)s' def is_subscribed(self): # LEGACY COMPAT: the 'or' here is for legacy compat return (self.state == 'subscribed' or self.state == None)
class Presence(CachingModel): """This represents all the presence data for an actor at a moment in time. extra: status - string; message (like an "away message") location - string; TODO(tyler): Consider gps / cell / structured data availability - string; TODO(tyler): Define structure """ actor = models.StringProperty() # The actor whose presence this is updated_at = properties.DateTimeProperty(auto_now_add=True) # The moment we got the update uuid = models.StringProperty() extra = properties.DictProperty() # All the rich presence
class Task(CachingModel): actor = models.StringProperty() # ref - the owner of this queue item action = models.StringProperty() # api call we are iterating through action_id = models.StringProperty() # unique identifier for this queue item args = models.StringListProperty() # *args kw = properties.DictProperty() # *kw expire = properties.DateTimeProperty() # when our lock will expire progress = models.StringProperty() # a string representing the offset to # which we've progressed so far created_at = properties.DateTimeProperty(auto_now_add=True) key_template = 'task/%(actor)s/%(action)s/%(action_id)s'
class Stream(DeletedMarkerModel): """ extra: see api.stream_create() """ owner = models.StringProperty() # ref title = models.StringProperty() type = models.StringProperty() slug = models.StringProperty() read = models.IntegerProperty() # TODO: document this write = models.IntegerProperty() extra = properties.DictProperty() key_template = 'stream/%(owner)s/%(slug)s' def is_public(self): return self.read == PRIVACY_PUBLIC def is_restricted(self): return self.read == PRIVACY_CONTACTS def keyname(self): """Returns the key name""" return self.key().name()
class StreamEntry(DeletedMarkerModel): """ extra : title - location - icon - content - entry_stream - entry_stream_type - entry_title - entry_uuid - comment_count - """ stream = models.StringProperty() # ref - the stream this belongs to owner = models.StringProperty() # ref - the actor who owns the stream actor = models.StringProperty() # ref - the actor who wrote this entry = models.StringProperty() # ref - the parent of this, # should it be a comment uuid = models.StringProperty() created_at = properties.DateTimeProperty(auto_now_add=True) extra = properties.DictProperty() key_template = '%(stream)s/%(uuid)s' def url(self, with_anchor=True, request=None, mobile=False): if self.entry: # TODO bad? slug = self.entry.split("/")[-1] anchor = "#c-%s" % self.uuid else: # TODO(termie): add slug property slug = self.uuid anchor = "" path = "/%s/%s" % ('presence', slug) if with_anchor: path = "%s%s" % (path, anchor) return actor_url(_get_actor_urlnick_from_nick(self.owner), _get_actor_type_from_nick(self.owner), path=path, request=request, mobile=mobile) def keyname(self): """Returns the key name""" return self.key().name() def title(self): """ build a title for this entry, for a presence entry it will just be the title, but for a comment it will look like: Comment from [commenter nick] on [entry title] by [nick] Comment from [commenter nick] on [entry title] by [nick] to #[channel name] """ if not self.is_comment(): return self.extra.get('title') template = "Comment from %(actor)s on %(entry_title)s by %(entry_actor)s" actor = _get_actor_urlnick_from_nick(self.actor) entry_title = self.extra.get('entry_title') entry_actor = _get_actor_urlnick_from_nick( self.extra.get('entry_actor')) entry_owner_nick = util.get_user_from_topic(self.entry) entry_type = _get_actor_type_from_nick(entry_owner_nick) v = { 'actor': actor, 'entry_title': entry_title, 'entry_actor': entry_actor, } if entry_type == 'channel': template += ' to #%(channel)s' channel = _get_actor_urlnick_from_nick(entry_owner_nick) v['channel'] = channel return template % v def is_comment(self): return (self.entry != None) def is_channel(self): return self.owner.startswith('#') def entry_actor(self): if self.entry: return util.get_user_from_topic(self.entry) return None
class Actor(DeletedMarkerModel): """ extra: contact_count - int; number of contacts follower_count - int; number of followers icon - string; avatar path bg_image - string; image for background (takes precedence over bg_color) bg_color - string; color for background bg_repeat - whether to repeat bg_image description [channel] - string; Channel description external_url [channel] - string; External url related ot channel member_count [channel] - int; number of members admin_count [channel] - int; number of admins email_notify [user] - boolean; does the user want email notifications? given_name [user] - string; First name family_name [user] - string; Last Name comments_hide [user] - boolean; Whether comments should be hidden on overview """ nick = models.StringProperty() # the appengine datastore is case-sensitive whereas human brains are not, # Paul is not different from paul to regular people so we need a way to # prevent duplicate names from cropping up, this adds an additional indexed # property to support that normalized_nick = models.StringProperty() password = models.StringProperty() privacy = models.IntegerProperty() type = models.StringProperty() extra = properties.DictProperty() # avatar_updated_at is used by DJabberd to get a list of changed avatar. We # set the default to a date before the launch so that initial avatars have an # updated_at that is less than any real changes. avatar_updated_at = properties.DateTimeProperty( default=datetime.datetime(2009, 01, 01)) key_template = 'actor/%(nick)s' def url(self, path="", request=None, mobile=False): """ returns a url, with optional path appended NOTE: if appending a path, it should start with '/' """ return actor_url(_get_actor_urlnick_from_nick(self.nick), self.type, path=path, request=request, mobile=mobile) def shortnick(self): return _get_actor_urlnick_from_nick(self.nick) def display_nick(self): return self.nick.split("@")[0] return _get_actor_urlnick_from_nick(self.nick) def to_api(self): rv = super(Actor, self).to_api() del rv['password'] del rv['normalized_nick'] extra = {} for k, v in rv['extra'].iteritems(): if k in ACTOR_ALLOWED_EXTRA: extra[k] = v rv['extra'] = extra return rv def to_api_limited(self): rv = self.to_api() extra = {} for k, v in rv['extra'].iteritems(): if k in ACTOR_LIMITED_EXTRA: extra[k] = v rv['extra'] = extra return rv def is_channel(self): return self.type == 'channel' def is_public(self): return self.privacy == PRIVACY_PUBLIC def is_restricted(self): return self.privacy == PRIVACY_CONTACTS def __repr__(self): # Get all properties, but not directly as property objects, because # constructor requires values to be passed in. d = dict([(k, self.__getattribute__(k)) for k in self.properties().keys()]) return "%s(**%s)" % (self.__class__.__name__, repr(d))