From 71b585bb614d80e79c8866b46145c6bdf6e95f20 Mon Sep 17 00:00:00 2001
From: Victor Shnayder <victor@mitx.mit.edu>
Date: Sat, 17 Nov 2012 16:33:00 -0500
Subject: [PATCH] move unique_id_for_user into student/models.py

---
 common/djangoapps/student/models.py | 47 +++++++++++++++++++----------
 common/djangoapps/student/tests.py  |  6 ++--
 common/djangoapps/student/views.py  |  6 +---
 3 files changed, 36 insertions(+), 23 deletions(-)

diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py
index 0eded21df10..4c91682ca66 100644
--- a/common/djangoapps/student/models.py
+++ b/common/djangoapps/student/models.py
@@ -36,10 +36,12 @@ file and check it in at the same time as your model changes. To do that,
 3. Add the migration file created in mitx/common/djangoapps/student/migrations/
 """
 from datetime import datetime
+from hashlib import sha1
 import json
 import logging
 import uuid
 
+
 from django.conf import settings
 from django.contrib.auth.models import User
 from django.db import models
@@ -125,9 +127,9 @@ class UserProfile(models.Model):
         self.meta = json.dumps(js)
 
 class TestCenterUser(models.Model):
-    """This is our representation of the User for in-person testing, and 
+    """This is our representation of the User for in-person testing, and
     specifically for Pearson at this point. A few things to note:
-    
+
     * Pearson only supports Latin-1, so we have to make sure that the data we
       capture here will work with that encoding.
     * While we have a lot of this demographic data in UserProfile, it's much
@@ -135,9 +137,9 @@ class TestCenterUser(models.Model):
       UserProfile, but we'll need to have a step where people who are signing
       up re-enter their demographic data into the fields we specify.
     * Users are only created here if they register to take an exam in person.
-    
+
     The field names and lengths are modeled on the conventions and constraints
-    of Pearson's data import system, including oddities such as suffix having 
+    of Pearson's data import system, including oddities such as suffix having
     a limit of 255 while last_name only gets 50.
     """
     # Our own record keeping...
@@ -148,21 +150,21 @@ class TestCenterUser(models.Model):
     # and is something Pearson needs to know to manage updates. Unlike
     # updated_at, this will not get incremented when we do a batch data import.
     user_updated_at = models.DateTimeField(db_index=True)
-    
+
     # Unique ID given to us for this User by the Testing Center. It's null when
     # we first create the User entry, and is assigned by Pearson later.
     candidate_id = models.IntegerField(null=True, db_index=True)
-    
+
     # Unique ID we assign our user for a the Test Center.
     client_candidate_id = models.CharField(max_length=50, db_index=True)
-    
+
     # Name
     first_name = models.CharField(max_length=30, db_index=True)
     last_name = models.CharField(max_length=50, db_index=True)
     middle_name = models.CharField(max_length=30, blank=True)
     suffix = models.CharField(max_length=255, blank=True)
     salutation = models.CharField(max_length=50, blank=True)
-    
+
     # Address
     address_1 = models.CharField(max_length=40)
     address_2 = models.CharField(max_length=40, blank=True)
@@ -175,7 +177,7 @@ class TestCenterUser(models.Model):
     postal_code = models.CharField(max_length=16, blank=True, db_index=True)
     # country is a ISO 3166-1 alpha-3 country code (e.g. "USA", "CAN", "MNG")
     country = models.CharField(max_length=3, db_index=True)
-    
+
     # Phone
     phone = models.CharField(max_length=35)
     extension = models.CharField(max_length=8, blank=True, db_index=True)
@@ -183,14 +185,27 @@ class TestCenterUser(models.Model):
     fax = models.CharField(max_length=35, blank=True)
     # fax_country_code required *if* fax is present.
     fax_country_code = models.CharField(max_length=3, blank=True)
-    
+
     # Company
     company_name = models.CharField(max_length=50, blank=True)
-    
+
     @property
     def email(self):
         return self.user.email
 
+def unique_id_for_user(user):
+    """
+    Return a unique id for a user, suitable for inserting into
+    e.g. personalized survey links.
+
+    Currently happens to be implemented as a sha1 hash of the username
+    (and thus assumes that usernames don't change).
+    """
+    return sha1(user.username).hexdigest()
+
+
+
+
 ## TODO: Should be renamed to generic UserGroup, and possibly
 # Given an optional field for type of group
 class UserTestGroup(models.Model):
@@ -363,10 +378,10 @@ def replicate_user_save(sender, **kwargs):
 
 # @receiver(post_save, sender=CourseEnrollment)
 def replicate_enrollment_save(sender, **kwargs):
-    """This is called when a Student enrolls in a course. It has to do the 
+    """This is called when a Student enrolls in a course. It has to do the
     following:
 
-    1. Make sure the User is copied into the Course DB. It may already exist 
+    1. Make sure the User is copied into the Course DB. It may already exist
        (someone deleting and re-adding a course). This has to happen first or
        the foreign key constraint breaks.
     2. Replicate the CourseEnrollment.
@@ -410,9 +425,9 @@ USER_FIELDS_TO_COPY = ["id", "username", "first_name", "last_name", "email",
 
 def replicate_user(portal_user, course_db_name):
     """Replicate a User to the correct Course DB. This is more complicated than
-    it should be because Askbot extends the auth_user table and adds its own 
+    it should be because Askbot extends the auth_user table and adds its own
     fields. So we need to only push changes to the standard fields and leave
-    the rest alone so that Askbot changes at the Course DB level don't get 
+    the rest alone so that Askbot changes at the Course DB level don't get
     overridden.
     """
     try:
@@ -457,7 +472,7 @@ def is_valid_course_id(course_id):
     """Right now, the only database that's not a course database is 'default'.
     I had nicer checking in here originally -- it would scan the courses that
     were in the system and only let you choose that. But it was annoying to run
-    tests with, since we don't have course data for some for our course test 
+    tests with, since we don't have course data for some for our course test
     databases. Hence the lazy version.
     """
     return course_id != 'default'
diff --git a/common/djangoapps/student/tests.py b/common/djangoapps/student/tests.py
index 16eec863799..4c7c9e25928 100644
--- a/common/djangoapps/student/tests.py
+++ b/common/djangoapps/student/tests.py
@@ -12,8 +12,10 @@ from django.test import TestCase
 from mock import patch, Mock
 from nose.plugins.skip import SkipTest
 
-from .models import User, UserProfile, CourseEnrollment, replicate_user, USER_FIELDS_TO_COPY
-from .views import process_survey_link, _cert_info, unique_id_for_user
+from .models import (User, UserProfile, CourseEnrollment,
+                     replicate_user, USER_FIELDS_TO_COPY,
+                     unique_id_for_user)
+from .views import process_survey_link, _cert_info
 
 COURSE_1 = 'edX/toy/2012_Fall'
 COURSE_2 = 'edx/full/6.002_Spring_2012'
diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py
index b354b8c20ab..ac3a97bd895 100644
--- a/common/djangoapps/student/views.py
+++ b/common/djangoapps/student/views.py
@@ -28,7 +28,7 @@ from django.core.cache import cache
 from django_future.csrf import ensure_csrf_cookie, csrf_exempt
 from student.models import (Registration, UserProfile,
                             PendingNameChange, PendingEmailChange,
-                            CourseEnrollment)
+                            CourseEnrollment, unique_id_for_user)
 
 from certificates.models import CertificateStatuses, certificate_status_for_student
 
@@ -39,7 +39,6 @@ from xmodule.modulestore.exceptions import ItemNotFoundError
 
 from datetime import date
 from collections import namedtuple
-from hashlib import sha1
 
 from courseware.courses import get_courses_by_university
 from courseware.access import has_access
@@ -129,9 +128,6 @@ def press(request):
     return render_to_response('static_templates/press.html', {'articles': articles})
 
 
-def unique_id_for_user(user):
-    return sha1(user.username).hexdigest()
-
 def process_survey_link(survey_link, user):
     """
     If {UNIQUE_ID} appears in the link, replace it with a unique id for the user.
-- 
GitLab