Add helper functions, model update and jobs for unique user ids.
authorPawel Solyga <Pawel.Solyga@gmail.com>
Mon, 25 May 2009 22:59:56 +0200
changeset 2345 f78caf12f32d
parent 2344 621252e2cc18
child 2346 d830123c1be1
child 2347 c5a397f57d65
Add helper functions, model update and jobs for unique user ids.
app/soc/cron/job.py
app/soc/cron/unique_user_id_adder.py
app/soc/logic/accounts.py
app/soc/logic/models/user.py
app/soc/models/user.py
scripts/stats.py
--- a/app/soc/cron/job.py	Mon May 25 22:59:33 2009 +0200
+++ b/app/soc/cron/job.py	Mon May 25 22:59:56 2009 +0200
@@ -29,8 +29,10 @@
 from google.appengine.runtime import DeadlineExceededError
 
 from soc.cron import student_proposal_mailer
+from soc.cron import unique_user_id_adder
 from soc.models.job import Job
 
+
 class Error(Exception):
   """Base class for all exceptions raised by this module.
   """
@@ -67,6 +69,10 @@
         student_proposal_mailer.setupStudentProposalMailing
     self.tasks['sendStudentProposalMail'] = \
         student_proposal_mailer.sendStudentProposalMail
+    self.tasks['setupUniqueUserIdAdder'] = \
+        unique_user_id_adder.setupUniqueUserIdAdder
+    self.tasks['addUniqueUserIds'] = \
+        unique_user_id_adder.addUniqueUserIds
 
   def claimJob(self, job_key):
     """A transaction to claim a job.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/cron/unique_user_id_adder.py	Mon May 25 22:59:56 2009 +0200
@@ -0,0 +1,135 @@
+#!/usr/bin/python2.5
+#
+# Copyright 2009 the Melange authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Cron job handler for adding unique user id.
+"""
+
+__authors__ = [
+    '"Pawel Solyga" <pawel.solyga@gmail.com>',
+  ]
+
+
+from google.appengine.ext import db
+from google.appengine.api import users
+from soc.logic.models.job import logic as job_logic
+from soc.logic.models.priority_group import logic as priority_logic
+from soc.logic.models.user import logic as user_logic
+from soc.models.user import User
+
+
+# amount of users to create jobs for before updating
+DEF_USER_STEP_SIZE = 10
+
+
+class TempUserWithUniqueId(db.Model):
+  """Helper model for temporary storing User Property with unique id.
+  """
+  user = db.UserProperty(required=True)
+
+
+def emailToAccountAndUserId(address):
+  """Return a stable user_id string based on an email address, or None if
+  the address is not a valid/existing google account.
+  """
+  user = users.User(address)
+  key = TempUserWithUniqueId(user=user).put()
+  obj = TempUserWithUniqueId.get(key)
+  return (obj, obj.user.user_id())
+
+
+def setupUniqueUserIdAdder(job_entity):
+  """Job that setup jobs that will add unique user ids to all Users.
+
+  Args:
+    job_entity: a Job entity with key_data set to 
+                [last_completed_user]
+  """
+
+  from soc.cron.job import FatalJobError
+
+  user_fields = {'user_id': None}
+
+  if len(key_data) == 1:
+    # start where we left off
+    user_fields['__key__ >'] = key_data[0]
+
+  m_users = user_logic.getForFields(user_fields,
+                                    limit=DEF_USER_STEP_SIZE)
+
+  # set the default fields for the jobs we are going to create
+  priority_group = priority_logic.getGroup(priority_logic.CONVERT)
+  job_fields = {
+      'priority_group': priority_group,
+      'task_name': 'addUniqueUserIds'}
+
+  job_query_fields = job_fields.copy()
+
+  while m_users:
+    # for each user create a adder job
+    for user in m_users:
+
+      job_query_fields['key_data'] = user.key()
+      adder_job = job_logic.getForFields(job_query_fields, unique=True)
+
+      if not adder_job:
+        # this user doesn't have unique id yet
+        job_fields['key_data'] = [user.key()]
+        job_logic.updateOrCreateFromFields(job_fields)
+
+    # update our own job
+    last_user_key = m_users[-1].key()
+
+    if len(key_data) == 1:
+      key_data[0] = last_student_key
+    else:
+      key_data.append(last_student_key)
+
+    updated_job_fields = {'key_data': key_data}
+    job_logic.updateEntityProperties(job_entity, updated_job_fields)
+
+    # rinse and repeat
+    user_fields['__key__ >'] = last_user_key
+    m_users = student_logic.getForFields(user_fields,
+                                         limit=DEF_USER_STEP_SIZE)
+
+  # we are finished
+  return
+
+
+def addUniqueUserIds(job_entity):
+  """Job that will add unique user id to a User.
+
+  Args:
+    job_entity: a Job entity with key_data set to [user_key]
+  """
+
+  from soc.cron.job import FatalJobError
+
+  user_keyname = job_entity.key_data[0].name()
+  user_entity = user_logic.getFromKeyName(user_keyname)
+
+  if not user_entity:
+    raise FatalJobError('The User with keyname %s does not exist!' % (
+        user_keyname))
+
+  # add unique user id
+  account, user_id = emailToAccountAndUserId(user_entity.account.email())
+  user_entity.account = account
+  user_entity.user_id = user_id
+  user_entity.put()
+  
+  # we are done here
+  return
\ No newline at end of file
--- a/app/soc/logic/accounts.py	Mon May 25 22:59:33 2009 +0200
+++ b/app/soc/logic/accounts.py	Mon May 25 22:59:56 2009 +0200
@@ -35,6 +35,13 @@
   return normalizeAccount(account) if (account and normalize) else account
 
 
+def getCurrentUserId():
+  """Returns a unique id of the current user.
+  """
+
+  return users.get_current_user().user_id()
+
+
 def normalizeAccount(account):
   """Returns a normalized version of the specified account.
   """
@@ -46,6 +53,7 @@
 
   return users.User(email=normalized)
 
+
 def denormalizeAccount(account):
   """Returns a denormalized version of the specified account.
   """
@@ -58,6 +66,7 @@
 
   return users.User(email=denormalized)
 
+
 def isDeveloper(account=None):
   """Returns True if a Google Account is a Developer with special privileges.
 
--- a/app/soc/logic/models/user.py	Mon May 25 22:59:33 2009 +0200
+++ b/app/soc/logic/models/user.py	Mon May 25 22:59:56 2009 +0200
@@ -75,6 +75,20 @@
 
     return self.getForAccount(account)
 
+  def getForCurrentUserId(self):
+    """Retrieves the user entity for the currently logged in user id.
+
+    If there is no user logged in, or they have no valid associated User
+    entity, None is returned.
+    """
+
+    user_id = accounts.getCurrentUserId()
+
+    if not user_id:
+      return None
+
+    return self.getForUserId(user_id)
+
   def getForAccount(self, account):
     """Retrieves the user entity for the specified account.
 
@@ -94,6 +108,23 @@
 
     return self.getForFields(filter=fields, unique=True)
 
+  def getForUserId(self, user_id):
+    """Retrieves the user entity for the specified user id.
+
+    If there is no user logged in, or they have no valid associated User
+    entity, None is returned.
+    """
+
+    if not user_id:
+      raise base.InvalidArgumentError
+
+    fields = {
+        'user_id': user_id,
+        'status':'valid',
+        }
+
+    return self.getForFields(filter=fields, unique=True)
+
   def isDeveloper(self, account=None, user=None):
     """Returns true iff the specified user is a Developer.
 
--- a/app/soc/models/user.py	Mon May 25 22:59:33 2009 +0200
+++ b/app/soc/models/user.py	Mon May 25 22:59:56 2009 +0200
@@ -71,6 +71,9 @@
       verbose_name=ugettext('User account'))
   account.help_text = ugettext(
       'A valid Google Account.')
+  
+  #: Google Account unique user id
+  user_id = db.StringProperty(required=True)
 
   #: A list (possibly empty) of former Google Accounts associated with
   #: this User.
--- a/scripts/stats.py	Mon May 25 22:59:33 2009 +0200
+++ b/scripts/stats.py	Mon May 25 22:59:56 2009 +0200
@@ -277,6 +277,21 @@
   job_logic.updateOrCreateFromFields(job_fields)
 
 
+def startUniqueUserIdConversion():
+  """Creates the job that is responsible for adding unique user ids.
+  """
+
+  from soc.logic.models.job import logic as job_logic
+  from soc.logic.models.priority_group import logic as priority_logic
+
+  priority_group = priority_logic.getGroup(priority_logic.CONVERT)
+  job_fields = {
+      'priority_group': priority_group,
+      'task_name': 'setupUniqueUserIdAdder'}
+
+  job_logic.updateOrCreateFromFields(job_fields)
+
+
 def reviveJobs(amount):
   """Sets jobs that are stuck in 'aborted' to waiting.
 
@@ -493,6 +508,7 @@
       'reviveJobs': reviveJobs,
       'deidleJobs': deidleJobs,
       'acceptedStudentsCSVExport': acceptedStudentsCSVExport,
+      'startUniqueUserIdConversion': startUniqueUserIdConversion,
   }
 
   interactive.remote(args, context)