diff --git a/lms/djangoapps/student/migrations/0011_auto__chg_field_userprofile_country.py b/common/djangoapps/student/migrations/0019_create_approved_demographic_fields_fall_2012.py similarity index 71% rename from lms/djangoapps/student/migrations/0011_auto__chg_field_userprofile_country.py rename to common/djangoapps/student/migrations/0019_create_approved_demographic_fields_fall_2012.py index 47909cef49..d260e263f7 100644 --- a/lms/djangoapps/student/migrations/0011_auto__chg_field_userprofile_country.py +++ b/common/djangoapps/student/migrations/0019_create_approved_demographic_fields_fall_2012.py @@ -8,14 +8,70 @@ from django.db import models class Migration(SchemaMigration): def forwards(self, orm): + # Deleting field 'UserProfile.occupation' + db.delete_column('auth_userprofile', 'occupation') + + # Deleting field 'UserProfile.telephone_number' + db.delete_column('auth_userprofile', 'telephone_number') + + # Deleting field 'UserProfile.date_of_birth' + db.delete_column('auth_userprofile', 'date_of_birth') + + # Deleting field 'UserProfile.country' + db.delete_column('auth_userprofile', 'country') + + # Adding field 'UserProfile.year_of_birth' + db.add_column('auth_userprofile', 'year_of_birth', + self.gf('django.db.models.fields.IntegerField')(db_index=True, null=True, blank=True), + keep_default=False) + + # Adding field 'UserProfile.level_of_education' + db.add_column('auth_userprofile', 'level_of_education', + self.gf('django.db.models.fields.CharField')(db_index=True, max_length=6, null=True, blank=True), + keep_default=False) + + # Adding field 'UserProfile.goals' + db.add_column('auth_userprofile', 'goals', + self.gf('django.db.models.fields.TextField')(null=True, blank=True), + keep_default=False) + + # Adding index on 'UserProfile', fields ['gender'] + db.create_index('auth_userprofile', ['gender']) - # Changing field 'UserProfile.country' - db.alter_column('auth_userprofile', 'country', self.gf('django_countries.fields.CountryField')(max_length=2, null=True)) def backwards(self, orm): + # Removing index on 'UserProfile', fields ['gender'] + db.delete_index('auth_userprofile', ['gender']) + + # Adding field 'UserProfile.occupation' + db.add_column('auth_userprofile', 'occupation', + self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True), + keep_default=False) + + # Adding field 'UserProfile.telephone_number' + db.add_column('auth_userprofile', 'telephone_number', + self.gf('django.db.models.fields.CharField')(max_length=25, null=True, blank=True), + keep_default=False) + + # Adding field 'UserProfile.date_of_birth' + db.add_column('auth_userprofile', 'date_of_birth', + self.gf('django.db.models.fields.DateField')(null=True, blank=True), + keep_default=False) + + # Adding field 'UserProfile.country' + db.add_column('auth_userprofile', 'country', + self.gf('django_countries.fields.CountryField')(max_length=2, null=True, blank=True), + keep_default=False) + + # Deleting field 'UserProfile.year_of_birth' + db.delete_column('auth_userprofile', 'year_of_birth') + + # Deleting field 'UserProfile.level_of_education' + db.delete_column('auth_userprofile', 'level_of_education') + + # Deleting field 'UserProfile.goals' + db.delete_column('auth_userprofile', 'goals') - # Changing field 'UserProfile.country' - db.alter_column('auth_userprofile', 'country', self.gf('django.db.models.fields.CharField')(max_length=255, null=True)) models = { 'auth.group': { @@ -80,8 +136,9 @@ class Migration(SchemaMigration): 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) }, 'student.courseenrollment': { - 'Meta': {'object_name': 'CourseEnrollment'}, - 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'Meta': {'unique_together': "(('user', 'course_id'),)", 'object_name': 'CourseEnrollment'}, + 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) }, @@ -107,19 +164,18 @@ class Migration(SchemaMigration): }, 'student.userprofile': { 'Meta': {'object_name': 'UserProfile', 'db_table': "'auth_userprofile'"}, - 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'null': 'True', 'blank': 'True'}), 'courseware': ('django.db.models.fields.CharField', [], {'default': "'course.xml'", 'max_length': '255', 'blank': 'True'}), - 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'gender': ('django.db.models.fields.CharField', [], {'max_length': '6', 'null': 'True', 'blank': 'True'}), + 'gender': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '6', 'null': 'True', 'blank': 'True'}), + 'goals': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'language': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}), + 'level_of_education': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '6', 'null': 'True', 'blank': 'True'}), 'location': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}), 'mailing_address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'meta': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'meta': ('django.db.models.fields.TextField', [], {'blank': 'True'}), 'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}), - 'occupation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'telephone_number': ('django.db.models.fields.CharField', [], {'max_length': '25', 'null': 'True', 'blank': 'True'}), - 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"}) + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"}), + 'year_of_birth': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}) }, 'student.usertestgroup': { 'Meta': {'object_name': 'UserTestGroup'}, diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index 70619311e4..ca5081254e 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -5,14 +5,15 @@ If you make changes to this model, be sure to create an appropriate migration file and check it in at the same time as your model changes. To do that, 1. Go to the mitx dir -2. ./manage.py schemamigration user --auto description_of_your_change -3. Add the migration file created in mitx/courseware/migrations/ +2. django-admin.py schemamigration student --auto --settings=lms.envs.dev --pythonpath=. description_of_your_change +3. Add the migration file created in mitx/common/djangoapps/student/migrations/ """ +from datetime import datetime +import json import uuid from django.db import models from django.contrib.auth.models import User -import json from django_countries import CountryField #from cache_toolbox import cache_model, cache_relation @@ -21,23 +22,43 @@ class UserProfile(models.Model): class Meta: db_table = "auth_userprofile" - GENDER_CHOICES = (('m', 'Male'), ('f', 'Female'), ('o', 'Other')) - ## CRITICAL TODO/SECURITY # Sanitize all fields. # This is not visible to other users, but could introduce holes later user = models.OneToOneField(User, unique=True, db_index=True, related_name='profile') name = models.CharField(blank=True, max_length=255, db_index=True) - language = models.CharField(blank=True, max_length=255, db_index=True) - location = models.CharField(blank=True, max_length=255, db_index=True) # TODO: What are we doing with this? + meta = models.TextField(blank=True) # JSON dictionary for future expansion courseware = models.CharField(blank=True, max_length=255, default='course.xml') - gender = models.CharField(blank=True, null=True, max_length=6, choices=GENDER_CHOICES) - date_of_birth = models.DateField(blank=True, null=True) + + # Location is no longer used, but is held here for backwards compatibility + # for users imported from our first class. + language = models.CharField(blank=True, max_length=255, db_index=True) + location = models.CharField(blank=True, max_length=255, db_index=True) + + # Optional demographic data we started capturing from Fall 2012 + this_year = datetime.now().year + VALID_YEARS = range(this_year, this_year - 120, -1) + year_of_birth = models.IntegerField(blank=True, null=True, db_index=True) + GENDER_CHOICES = (('m', 'Male'), ('f', 'Female'), ('o', 'Other')) + gender = models.CharField(blank=True, null=True, max_length=6, db_index=True, + choices=GENDER_CHOICES) + LEVEL_OF_EDUCATION_CHOICES = (('p_se', 'Doctorate in science or engineering'), + ('p_oth', 'Doctorate in another field'), + ('m', "Master's or professional degree"), + ('b', "Bachelor's degree"), + ('hs', "Secondary/high school"), + ('jhs', "Junior secondary/junior high/middle school"), + ('el', "Elementary/primary school"), + ('none', "None"), + ('other', "Other")) + level_of_education = models.CharField( + blank=True, null=True, max_length=6, db_index=True, + choices=LEVEL_OF_EDUCATION_CHOICES + ) mailing_address = models.TextField(blank=True, null=True) - country = CountryField(blank=True, null=True) - telephone_number = models.CharField(blank=True, null=True, max_length=25) - occupation = models.CharField(blank=True, null=True, max_length=255) + goals = models.TextField(blank=True, null=True) + def get_meta(self): js_str = self.meta diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py index 309571ddb6..038606e556 100644 --- a/common/djangoapps/student/views.py +++ b/common/djangoapps/student/views.py @@ -306,17 +306,20 @@ def create_account(request, post_override=None): up = UserProfile(user=u) up.name = post_vars['name'] - up.country = post_vars['country'] + up.level_of_education = post_vars['level_of_education'] up.gender = post_vars['gender'] up.mailing_address = post_vars['mailing_address'] - - date_fields = ['date_of_birth__year', 'date_of_birth__month', 'date_of_birth__day'] - if all(len(post_vars[field]) > 0 for field in date_fields): - up.date_of_birth = date(int(post_vars['date_of_birth__year']), - int(post_vars['date_of_birth__month']), - int(post_vars['date_of_birth__day'])) - - up.save() + up.goals = post_vars['goals'] + + try: + up.year_of_birth = int(post_vars['year_of_birth']) + except ValueError: + up.year_of_birth = None # If they give us garbage, just ignore it instead + # of asking them to put an integer. + try: + up.save() + except Exception: + log.exception("UserProfile creation failed for user {0}.".format(u.id)) d = {'name': post_vars['name'], 'key': r.activation_key, diff --git a/lms/djangoapps/student/migrations/0012_auto__chg_field_userprofile_location.py b/lms/djangoapps/student/migrations/0012_auto__chg_field_userprofile_location.py deleted file mode 100644 index ea6fb91abc..0000000000 --- a/lms/djangoapps/student/migrations/0012_auto__chg_field_userprofile_location.py +++ /dev/null @@ -1,133 +0,0 @@ -# -*- coding: utf-8 -*- -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -class Migration(SchemaMigration): - - def forwards(self, orm): - - # Changing field 'UserProfile.location' - db.alter_column('auth_userprofile', 'location', self.gf('django.db.models.fields.CharField')(max_length=255, null=True)) - - def backwards(self, orm): - - # Changing field 'UserProfile.location' - db.alter_column('auth_userprofile', 'location', self.gf('django.db.models.fields.CharField')(default='', max_length=255)) - - models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'auth.user': { - 'Meta': {'object_name': 'User'}, - 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}), - 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), - 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), - 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}), - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), - 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), - 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}), - 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), - 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), - 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), - 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}), - 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) - }, - 'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'student.courseenrollment': { - 'Meta': {'object_name': 'CourseEnrollment'}, - 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) - }, - 'student.pendingemailchange': { - 'Meta': {'object_name': 'PendingEmailChange'}, - 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'new_email': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}), - 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}) - }, - 'student.pendingnamechange': { - 'Meta': {'object_name': 'PendingNameChange'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'new_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), - 'rationale': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}), - 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}) - }, - 'student.registration': { - 'Meta': {'object_name': 'Registration', 'db_table': "'auth_registration'"}, - 'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'}) - }, - 'student.userprofile': { - 'Meta': {'object_name': 'UserProfile', 'db_table': "'auth_userprofile'"}, - 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'null': 'True', 'blank': 'True'}), - 'courseware': ('django.db.models.fields.CharField', [], {'default': "'course.xml'", 'max_length': '255', 'blank': 'True'}), - 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'gender': ('django.db.models.fields.CharField', [], {'max_length': '6', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'language': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}), - 'location': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'mailing_address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'meta': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}), - 'occupation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'telephone_number': ('django.db.models.fields.CharField', [], {'max_length': '25', 'null': 'True', 'blank': 'True'}), - 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"}) - }, - 'student.usertestgroup': { - 'Meta': {'object_name': 'UserTestGroup'}, - 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), - 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'db_index': 'True', 'symmetrical': 'False'}) - } - } - - complete_apps = ['student'] \ No newline at end of file diff --git a/lms/static/sass/shared/_modal.scss b/lms/static/sass/shared/_modal.scss index 5e132a0993..aea82910c7 100644 --- a/lms/static/sass/shared/_modal.scss +++ b/lms/static/sass/shared/_modal.scss @@ -210,7 +210,7 @@ .citizenship, .gender { float: left; - width: flex-grid(6); + width: flex-grid(4); } .citizenship { diff --git a/lms/templates/signup_modal.html b/lms/templates/signup_modal.html index 164ce689b8..f456a4c214 100644 --- a/lms/templates/signup_modal.html +++ b/lms/templates/signup_modal.html @@ -27,18 +27,16 @@ - -
- +
- - %for country_code, country_name in COUNTRIES: - + %for code, ed_level in UserProfile.LEVEL_OF_EDUCATION_CHOICES: + %endfor
@@ -56,31 +54,24 @@
-
- +
+
- - - - - + + %for year in UserProfile.VALID_YEARS: + + %endfor + ##
+ + + + + +