def drafts(self): """A list of change ids that have drafts by this user. This is cached in memcache. """ if self._draft_key is None: self._draft_key = MemCacheKey(key='user_drafts:%s' % self.email, incore=True, timeout=3600) def query_store(): # We're looking for the Change key id. # The ancestry of comments goes: # Change -> PatchSet -> Patch -> Comment. # change_ids = set( comment.key().parent().parent().parent().id() for comment in gql(Comment, 'WHERE author = :1' ' AND draft = TRUE', self.user)) return list(change_ids) return self._draft_key.get(query_store)
def drafts(self): """A list of change ids that have drafts by this user. This is cached in memcache. """ if self._draft_key is None: self._draft_key = MemCacheKey( key = 'user_drafts:%s' % self.email, incore = True, timeout = 3600) def query_store(): # We're looking for the Change key id. # The ancestry of comments goes: # Change -> PatchSet -> Patch -> Comment. # change_ids = set(comment.key().parent().parent().parent().id() for comment in gql(Comment, 'WHERE author = :1' ' AND draft = TRUE', self.user)) return list(change_ids) return self._draft_key.get(query_store)
class Account(BackedUpModel): """Maps a user or email address to a user-selected real_name, and more. Nicknames do not have to be unique. The default real_name is generated from the email address by stripping the first '@' sign and everything after it. The email should not be empty nor should it start with '@' (AssertionError error is raised if either of these happens). Holds a list of ids of starred changes. The expectation that you won't have more than a dozen or so starred changes (a few hundred in extreme cases) and the memory used up by a list of integers of that size is very modest, so this is an efficient solution. (If someone found a use case for having thousands of starred changes we'd have to think of a different approach.) Returns whether a user is authorized to do lgtm or verify. For now, these authorization check methods do not test which repository the change is in. This will change. """ user = db.UserProperty(required=True) email = db.EmailProperty(required=True) # key == <email> preferred_email = db.EmailProperty() created = db.DateTimeProperty(auto_now_add=True) modified = db.DateTimeProperty(auto_now=True) is_admin = db.BooleanProperty(default=False) welcomed = db.BooleanProperty(default=False) real_name_entered = db.BooleanProperty(default=False) real_name = db.StringProperty() mailing_address = db.TextProperty() mailing_address_country = db.StringProperty() phone_number = db.StringProperty() fax_number = db.StringProperty() cla_verified = db.BooleanProperty(default=False) cla_verified_by = db.UserProperty() cla_verified_timestamp = db.DateTimeProperty() # the first time it's set individual_cla_version = db.IntegerProperty(default=IndividualCLA.NONE) individual_cla_timestamp = db.DateTimeProperty() cla_comments = db.TextProperty() default_context = db.IntegerProperty(default=DEFAULT_CONTEXT, choices=CONTEXT_CHOICES) stars = db.ListProperty(int) # Change ids of all starred changes unclaimed_changes_projects = db.ListProperty(db.Key) # Current user's Account. Updated by middleware.AddUserToRequestMiddleware. current_user_account = None def get_email(self): "Gets the email that this person wants us to use -- separate from login." if self.preferred_email: return self.preferred_email else: return self.email def get_email_formatted(self): return '"%s" <%s>' % (self.real_name, self.get_email()) @classmethod def get_account_for_user(cls, user): """Get the Account for a user, creating a default one if needed.""" email = user.email() assert email key = '<%s>' % email # Since usually the account already exists, first try getting it # without the transaction implied by get_or_insert(). account = cls.get_by_key_name(key) if account is not None: return account real_name = user.nickname() if '@' in real_name: real_name = real_name.split('@', 1)[0] assert real_name return cls.get_or_insert(key, user=user, email=email, real_name=real_name) @classmethod def get_account_for_email(cls, email): """Get the Account for an email address, or return None.""" assert email key = '<%s>' % email return cls.get_by_key_name(key) @classmethod def get_accounts_for_emails(cls, emails): """Get the Accounts for all email address. """ return cls.get_by_key_name(map(lambda x: '<%s>' % x, emails)) @classmethod def get_real_name_for_email(cls, email): """Get the real_name for an email address, possibly a default.""" account = cls.get_account_for_email(email) if account is not None and account.real_name: return account.real_name real_name = email if '@' in real_name: real_name = real_name.split('@', 1)[0] assert real_name return real_name @classmethod def get_accounts_for_real_name(cls, real_name): """Get the list of Accounts that have this real_name.""" assert real_name assert '@' not in real_name return list(gql(cls, 'WHERE real_name = :1', real_name)) @classmethod def get_email_for_real_name(cls, real_name): """Turn a real_name into an email address. If the real_name is not unique or does not exist, this returns None. """ accounts = cls.get_accounts_for_real_name(real_name) if len(accounts) != 1: return None return accounts[0].email _draft_key = None @property def drafts(self): """A list of change ids that have drafts by this user. This is cached in memcache. """ if self._draft_key is None: self._draft_key = MemCacheKey( key = 'user_drafts:%s' % self.email, incore = True, timeout = 3600) def query_store(): # We're looking for the Change key id. # The ancestry of comments goes: # Change -> PatchSet -> Patch -> Comment. # change_ids = set(comment.key().parent().parent().parent().id() for comment in gql(Comment, 'WHERE author = :1' ' AND draft = TRUE', self.user)) return list(change_ids) return self._draft_key.get(query_store) def update_drafts(self, change, have_drafts=None): """Update the user's draft status for this change. Args: change: an Change instance. have_drafts: optional bool forcing the draft status. By default, change.num_drafts is inspected (which may query the datastore). The Account is written to the datastore if necessary. """ my_drafts = self.drafts id = change.key().id() if have_drafts is None: have_drafts = bool(change.num_drafts) # this may do a query if have_drafts: if id not in my_drafts: my_drafts.append(id) self._draft_key.set(my_drafts) else: if id in my_drafts: my_drafts.remove(id) self._draft_key.set(my_drafts)
class Account(BackedUpModel): """Maps a user or email address to a user-selected real_name, and more. Nicknames do not have to be unique. The default real_name is generated from the email address by stripping the first '@' sign and everything after it. The email should not be empty nor should it start with '@' (AssertionError error is raised if either of these happens). Holds a list of ids of starred changes. The expectation that you won't have more than a dozen or so starred changes (a few hundred in extreme cases) and the memory used up by a list of integers of that size is very modest, so this is an efficient solution. (If someone found a use case for having thousands of starred changes we'd have to think of a different approach.) Returns whether a user is authorized to do lgtm or verify. For now, these authorization check methods do not test which repository the change is in. This will change. """ user = db.UserProperty(required=True) email = db.EmailProperty(required=True) # key == <email> preferred_email = db.EmailProperty() created = db.DateTimeProperty(auto_now_add=True) modified = db.DateTimeProperty(auto_now=True) is_admin = db.BooleanProperty(default=False) welcomed = db.BooleanProperty(default=False) real_name_entered = db.BooleanProperty(default=False) real_name = db.StringProperty() mailing_address = db.TextProperty() mailing_address_country = db.StringProperty() phone_number = db.StringProperty() fax_number = db.StringProperty() cla_verified = db.BooleanProperty(default=False) cla_verified_by = db.UserProperty() cla_verified_timestamp = db.DateTimeProperty() # the first time it's set individual_cla_version = db.IntegerProperty(default=IndividualCLA.NONE) individual_cla_timestamp = db.DateTimeProperty() cla_comments = db.TextProperty() default_context = db.IntegerProperty(default=DEFAULT_CONTEXT, choices=CONTEXT_CHOICES) stars = db.ListProperty(int) # Change ids of all starred changes unclaimed_changes_projects = db.ListProperty(db.Key) # Current user's Account. Updated by middleware.AddUserToRequestMiddleware. current_user_account = None def get_email(self): "Gets the email that this person wants us to use -- separate from login." if self.preferred_email: return self.preferred_email else: return self.email def get_email_formatted(self): return '"%s" <%s>' % (self.real_name, self.get_email()) @classmethod def get_account_for_user(cls, user): """Get the Account for a user, creating a default one if needed.""" email = user.email() assert email key = '<%s>' % email # Since usually the account already exists, first try getting it # without the transaction implied by get_or_insert(). account = cls.get_by_key_name(key) if account is not None: return account real_name = user.nickname() if '@' in real_name: real_name = real_name.split('@', 1)[0] assert real_name return cls.get_or_insert(key, user=user, email=email, real_name=real_name) @classmethod def get_account_for_email(cls, email): """Get the Account for an email address, or return None.""" assert email key = '<%s>' % email return cls.get_by_key_name(key) @classmethod def get_accounts_for_emails(cls, emails): """Get the Accounts for all email address. """ return cls.get_by_key_name(map(lambda x: '<%s>' % x, emails)) @classmethod def get_real_name_for_email(cls, email): """Get the real_name for an email address, possibly a default.""" account = cls.get_account_for_email(email) if account is not None and account.real_name: return account.real_name real_name = email if '@' in real_name: real_name = real_name.split('@', 1)[0] assert real_name return real_name @classmethod def get_accounts_for_real_name(cls, real_name): """Get the list of Accounts that have this real_name.""" assert real_name assert '@' not in real_name return list(gql(cls, 'WHERE real_name = :1', real_name)) @classmethod def get_email_for_real_name(cls, real_name): """Turn a real_name into an email address. If the real_name is not unique or does not exist, this returns None. """ accounts = cls.get_accounts_for_real_name(real_name) if len(accounts) != 1: return None return accounts[0].email _draft_key = None @property def drafts(self): """A list of change ids that have drafts by this user. This is cached in memcache. """ if self._draft_key is None: self._draft_key = MemCacheKey(key='user_drafts:%s' % self.email, incore=True, timeout=3600) def query_store(): # We're looking for the Change key id. # The ancestry of comments goes: # Change -> PatchSet -> Patch -> Comment. # change_ids = set( comment.key().parent().parent().parent().id() for comment in gql(Comment, 'WHERE author = :1' ' AND draft = TRUE', self.user)) return list(change_ids) return self._draft_key.get(query_store) def update_drafts(self, change, have_drafts=None): """Update the user's draft status for this change. Args: change: an Change instance. have_drafts: optional bool forcing the draft status. By default, change.num_drafts is inspected (which may query the datastore). The Account is written to the datastore if necessary. """ my_drafts = self.drafts id = change.key().id() if have_drafts is None: have_drafts = bool(change.num_drafts) # this may do a query if have_drafts: if id not in my_drafts: my_drafts.append(id) self._draft_key.set(my_drafts) else: if id in my_drafts: my_drafts.remove(id) self._draft_key.set(my_drafts)