From 7171397d95f822f150491546d5b4dddddb61f03f Mon Sep 17 00:00:00 2001 From: Adam Palay Date: Wed, 4 Dec 2013 10:23:04 -0500 Subject: [PATCH 01/14] extend max_length of anonymous_user_id (LMS-1571) --- ...field_anonymoususerid_anonymous_user_id.py | 198 ++++++++++++++++++ common/djangoapps/student/models.py | 2 +- 2 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 common/djangoapps/student/migrations/0030_auto__chg_field_anonymoususerid_anonymous_user_id.py diff --git a/common/djangoapps/student/migrations/0030_auto__chg_field_anonymoususerid_anonymous_user_id.py b/common/djangoapps/student/migrations/0030_auto__chg_field_anonymoususerid_anonymous_user_id.py new file mode 100644 index 0000000000..3a7d53c47c --- /dev/null +++ b/common/djangoapps/student/migrations/0030_auto__chg_field_anonymoususerid_anonymous_user_id.py @@ -0,0 +1,198 @@ +# -*- 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 'AnonymousUserId.anonymous_user_id' + db.alter_column('student_anonymoususerid', 'anonymous_user_id', self.gf('django.db.models.fields.CharField')(unique=True, max_length=32)) + + # THIS SQL WAS HAND-CODED + db.execute(""" + CREATE TABLE student_anonymoususerid_temp_archive + AS SELECT * FROM student_anonymoususerid WHERE LENGTH(anonymous_user_id) = 16 + """) + db.execute(""" + DELETE FROM student_anonymoususerid + WHERE LENGTH(anonymous_user_id) = 16 + """) + + def backwards(self, orm): + + # Changing field 'AnonymousUserId.anonymous_user_id' + db.alter_column('student_anonymoususerid', 'anonymous_user_id', self.gf('django.db.models.fields.CharField')(max_length=16, unique=True)) + + 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'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + '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'}) + }, + '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.anonymoususerid': { + 'Meta': {'object_name': 'AnonymousUserId'}, + 'anonymous_user_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}), + 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'student.courseenrollment': { + 'Meta': {'ordering': "('user', 'course_id')", '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'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'mode': ('django.db.models.fields.CharField', [], {'default': "'honor'", 'max_length': '100'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'student.courseenrollmentallowed': { + 'Meta': {'unique_together': "(('email', 'course_id'),)", 'object_name': 'CourseEnrollmentAllowed'}, + 'auto_enroll': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + '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'}), + 'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + '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.testcenterregistration': { + 'Meta': {'object_name': 'TestCenterRegistration'}, + 'accommodation_code': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'accommodation_request': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}), + 'authorization_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_index': 'True'}), + 'client_authorization_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20', 'db_index': 'True'}), + 'confirmed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}), + 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'eligibility_appointment_date_first': ('django.db.models.fields.DateField', [], {'db_index': 'True'}), + 'eligibility_appointment_date_last': ('django.db.models.fields.DateField', [], {'db_index': 'True'}), + 'exam_series_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'processed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}), + 'testcenter_user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['student.TestCenterUser']"}), + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'upload_error_message': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), + 'upload_status': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '20', 'blank': 'True'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}), + 'user_updated_at': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}) + }, + 'student.testcenteruser': { + 'Meta': {'object_name': 'TestCenterUser'}, + 'address_1': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'address_2': ('django.db.models.fields.CharField', [], {'max_length': '40', 'blank': 'True'}), + 'address_3': ('django.db.models.fields.CharField', [], {'max_length': '40', 'blank': 'True'}), + 'candidate_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_index': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'client_candidate_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}), + 'company_name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'confirmed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '3', 'db_index': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'extension': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '8', 'blank': 'True'}), + 'fax': ('django.db.models.fields.CharField', [], {'max_length': '35', 'blank': 'True'}), + 'fax_country_code': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'middle_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'phone': ('django.db.models.fields.CharField', [], {'max_length': '35'}), + 'phone_country_code': ('django.db.models.fields.CharField', [], {'max_length': '3', 'db_index': 'True'}), + 'postal_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '16', 'blank': 'True'}), + 'processed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}), + 'salutation': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '20', 'blank': 'True'}), + 'suffix': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'upload_error_message': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), + 'upload_status': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '20', 'blank': 'True'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['auth.User']", 'unique': 'True'}), + 'user_updated_at': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}) + }, + 'student.userprofile': { + 'Meta': {'object_name': 'UserProfile', 'db_table': "'auth_userprofile'"}, + 'allow_certificate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'courseware': ('django.db.models.fields.CharField', [], {'default': "'course.xml'", 'max_length': '255', '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.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}), + '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.userstanding': { + 'Meta': {'object_name': 'UserStanding'}, + 'account_status': ('django.db.models.fields.CharField', [], {'max_length': '31', 'blank': 'True'}), + 'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'standing_last_changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'standing'", '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/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index 15cc802b07..314e8ab043 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -53,7 +53,7 @@ class AnonymousUserId(models.Model): http://docs.python.org/2/library/md5.html#md5.digest_size """ user = models.ForeignKey(User, db_index=True) - anonymous_user_id = models.CharField(unique=True, max_length=16) + anonymous_user_id = models.CharField(unique=True, max_length=32) course_id = models.CharField(db_index=True, max_length=255) unique_together = (user, course_id) From 838622f5bd9068687364eb836924e68db29109da Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 4 Dec 2013 13:43:59 -0500 Subject: [PATCH 02/14] Handle integrity errors when creating/retrieving AnonymousUserId entries --- common/djangoapps/student/models.py | 30 +++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index 314e8ab043..731db5ac4b 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -20,7 +20,7 @@ import uuid from django.conf import settings from django.contrib.auth.models import User from django.contrib.auth.signals import user_logged_in, user_logged_out -from django.db import models +from django.db import models, IntegrityError from django.db.models.signals import post_save from django.dispatch import receiver import django.dispatch @@ -74,12 +74,30 @@ def anonymous_id_for_user(user, course_id): hasher.update(settings.SECRET_KEY) hasher.update(str(user.id)) hasher.update(course_id) + digest = hasher.hexdigest() - return AnonymousUserId.objects.get_or_create( - defaults={'anonymous_user_id': hasher.hexdigest()}, - user=user, - course_id=course_id - )[0].anonymous_user_id + try: + anonymous_user_id, created = AnonymousUserId.objects.get_or_create( + defaults={'anonymous_user_id': digest}, + user=user, + course_id=course_id + ) + if anonymous_user_id.anonymous_user_id != digest: + log.error( + "Stored anonymous user id {stored!r} for user {user!r} " + "in course {course!r} doesn't match computed id {digest!r}".format( + user=user, + course=course_id, + stored=anonymous_user_id.anonymous_user_id, + digest=digest + ) + ) + except IntegrityError: + # Another thread has already created this entry, so + # continue + pass + + return digest def user_by_anonymous_id(id): From eb23b65992919cb8d8be6b23a0e3edf3a775b3f9 Mon Sep 17 00:00:00 2001 From: Adam Palay Date: Wed, 4 Dec 2013 13:03:12 -0500 Subject: [PATCH 03/14] add temporary error message for ORA panel --- .../open_ended_problems/open_ended_problems.html | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lms/templates/open_ended_problems/open_ended_problems.html b/lms/templates/open_ended_problems/open_ended_problems.html index 3fe03bac0f..2167e31ce8 100644 --- a/lms/templates/open_ended_problems/open_ended_problems.html +++ b/lms/templates/open_ended_problems/open_ended_problems.html @@ -16,6 +16,17 @@
${error_text}
+
+
+ + ${_("We're sorry, some student responses have been temporarily " + "hidden. We appreciate your patience as we work hard " + "to restore their visibility. Rest assured, no student " + "data has been lost.")} + +
+
+

${_("Open Ended Problems")}

${_("Instructions")}

${_("Here is a list of open ended problems for this course.")}

@@ -48,4 +59,4 @@ %endif %endif
-
+ \ No newline at end of file From 175e1aab9efc2e622d7dd846262f944fb9974506 Mon Sep 17 00:00:00 2001 From: Adam Palay Date: Wed, 4 Dec 2013 14:30:09 -0500 Subject: [PATCH 04/14] adds more temporary error messages --- lms/templates/instructor/staff_grading.html | 14 +++++++++++++- .../combined_notifications.html | 12 ++++++++++++ .../open_ended_flagged_problems.html | 12 ++++++++++++ lms/templates/peer_grading/peer_grading.html | 11 +++++++++++ 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/lms/templates/instructor/staff_grading.html b/lms/templates/instructor/staff_grading.html index 5b23f83e50..c1710e7b89 100644 --- a/lms/templates/instructor/staff_grading.html +++ b/lms/templates/instructor/staff_grading.html @@ -22,9 +22,21 @@

${_("Staff grading")}

+
+
+ + ${_("We're sorry, some student responses have been temporarily " + "hidden. We appreciate your patience as we work hard " + "to restore their visibility. Rest assured, no student " + "data has been lost.")} + +
+
+
+
- +

${_("Instructions")}

diff --git a/lms/templates/open_ended_problems/combined_notifications.html b/lms/templates/open_ended_problems/combined_notifications.html index 4ac853076c..12776b8759 100644 --- a/lms/templates/open_ended_problems/combined_notifications.html +++ b/lms/templates/open_ended_problems/combined_notifications.html @@ -16,6 +16,18 @@
${error_text}
+
+
+ + ${_("We're sorry, some student responses have been temporarily " + "hidden. We appreciate your patience as we work hard " + "to restore their visibility. Rest assured, no student " + "data has been lost.")} + +
+
+
+

${_("Open Ended Console")}

${_("Instructions")}

${_("Here are items that could potentially need your attention.")}

diff --git a/lms/templates/open_ended_problems/open_ended_flagged_problems.html b/lms/templates/open_ended_problems/open_ended_flagged_problems.html index db83f75514..4e77c2651c 100644 --- a/lms/templates/open_ended_problems/open_ended_flagged_problems.html +++ b/lms/templates/open_ended_problems/open_ended_flagged_problems.html @@ -19,6 +19,18 @@
${error_text}
+
+
+ + ${_("We're sorry, some student responses have been temporarily " + "hidden. We appreciate your patience as we work hard " + "to restore their visibility. Rest assured, no student " + "data has been lost.")} + +
+
+
+

${_("Flagged Open Ended Problems")}

${_("Instructions")}

${_("Here are a list of open ended problems for this course that have been flagged by students as potentially inappropriate.")}

diff --git a/lms/templates/peer_grading/peer_grading.html b/lms/templates/peer_grading/peer_grading.html index 101e6341f5..3e2a421fcb 100644 --- a/lms/templates/peer_grading/peer_grading.html +++ b/lms/templates/peer_grading/peer_grading.html @@ -15,6 +15,17 @@ criteria.{end_li_tag}
${error_text}
+
+
+ + ${_("We're sorry, some student responses have been temporarily " + "hidden. We appreciate your patience as we work hard " + "to restore their visibility. Rest assured, no student " + "data has been lost.")} + +
+
+

${_("Peer Grading")}

${_("Instructions")}

From e4403815e7141ba9f1818b04e40cc6ceb56418f2 Mon Sep 17 00:00:00 2001 From: polesye Date: Tue, 3 Dec 2013 12:50:16 +0200 Subject: [PATCH 05/14] Fix LTI max_score method. --- common/lib/xmodule/xmodule/lti_module.py | 2 +- common/lib/xmodule/xmodule/tests/test_lti_unit.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/common/lib/xmodule/xmodule/lti_module.py b/common/lib/xmodule/xmodule/lti_module.py index 12bca979db..abc2c7084e 100644 --- a/common/lib/xmodule/xmodule/lti_module.py +++ b/common/lib/xmodule/xmodule/lti_module.py @@ -375,7 +375,7 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'} return params def max_score(self): - return self.weight + return self.weight if self.graded else 0 @XBlock.handler diff --git a/common/lib/xmodule/xmodule/tests/test_lti_unit.py b/common/lib/xmodule/xmodule/tests/test_lti_unit.py index b79bec16cc..2049366930 100644 --- a/common/lib/xmodule/xmodule/tests/test_lti_unit.py +++ b/common/lib/xmodule/xmodule/tests/test_lti_unit.py @@ -249,3 +249,13 @@ class LTIModuleTest(LogicTest): def test_client_key_secret(self): pass + def test_max_score(self): + self.xmodule.weight = 100.0 + + self.xmodule.graded = True + self.assertEqual(self.xmodule.max_score(), 100) + + self.xmodule.graded = False + self.assertEqual(self.xmodule.max_score(), 0) + + From c4515faedf52507788826009ab6a278f65db6299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Tue, 3 Dec 2013 17:48:27 +0200 Subject: [PATCH 06/14] Make has_score to be XField insted of decriptor property. Conflicts: common/lib/xmodule/xmodule/lti_module.py --- common/lib/xmodule/xmodule/lti_module.py | 4 ++-- common/lib/xmodule/xmodule/tests/test_lti_unit.py | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/common/lib/xmodule/xmodule/lti_module.py b/common/lib/xmodule/xmodule/lti_module.py index abc2c7084e..013d5e7ecb 100644 --- a/common/lib/xmodule/xmodule/lti_module.py +++ b/common/lib/xmodule/xmodule/lti_module.py @@ -89,6 +89,7 @@ class LTIFields(object): open_in_a_new_page = Boolean(help="Should LTI be opened in new page?", default=True, scope=Scope.settings) graded = Boolean(help="Grades will be considered in overall score.", default=False, scope=Scope.settings) weight = Float(help="Weight for student grades.", default=1.0, scope=Scope.settings) + has_score = Boolean(help="Does this LTI module have score?", default=False, scope=Scope.settings) class LTIModule(LTIFields, XModule): @@ -375,7 +376,7 @@ oauth_consumer_key="", oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"'} return params def max_score(self): - return self.weight if self.graded else 0 + return self.weight if self.has_score else None @XBlock.handler @@ -581,6 +582,5 @@ class LTIDescriptor(LTIFields, MetadataOnlyEditingDescriptor, EmptyDataRawDescri """ Descriptor for LTI Xmodule. """ - has_score = True module_class = LTIModule grade_handler = module_attr('grade_handler') diff --git a/common/lib/xmodule/xmodule/tests/test_lti_unit.py b/common/lib/xmodule/xmodule/tests/test_lti_unit.py index 2049366930..427a3c3ff2 100644 --- a/common/lib/xmodule/xmodule/tests/test_lti_unit.py +++ b/common/lib/xmodule/xmodule/tests/test_lti_unit.py @@ -253,9 +253,12 @@ class LTIModuleTest(LogicTest): self.xmodule.weight = 100.0 self.xmodule.graded = True - self.assertEqual(self.xmodule.max_score(), 100) + self.assertEqual(self.xmodule.max_score(), None) + + self.xmodule.has_score = True + self.assertEqual(self.xmodule.max_score(), 100.0) self.xmodule.graded = False - self.assertEqual(self.xmodule.max_score(), 0) + self.assertEqual(self.xmodule.max_score(), 100.0) From e96108629dbd545be20f58bcf7ffce71c9c68f03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Tue, 3 Dec 2013 19:04:21 +0200 Subject: [PATCH 07/14] Fix LTI tests. --- common/lib/xmodule/xmodule/tests/test_lti_unit.py | 1 + lms/djangoapps/courseware/features/lti.feature | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/common/lib/xmodule/xmodule/tests/test_lti_unit.py b/common/lib/xmodule/xmodule/tests/test_lti_unit.py index 427a3c3ff2..bb04b20868 100644 --- a/common/lib/xmodule/xmodule/tests/test_lti_unit.py +++ b/common/lib/xmodule/xmodule/tests/test_lti_unit.py @@ -194,6 +194,7 @@ class LTIModuleTest(LogicTest): Response from Tool Provider is correct. """ self.xmodule.verify_oauth_body_sign = Mock() + self.xmodule.has_score = True request = Request(self.environ) request.body = self.get_request_body() response = self.xmodule.grade_handler(request, '') diff --git a/lms/djangoapps/courseware/features/lti.feature b/lms/djangoapps/courseware/features/lti.feature index 7f27a65b5a..eb2da7436b 100644 --- a/lms/djangoapps/courseware/features/lti.feature +++ b/lms/djangoapps/courseware/features/lti.feature @@ -44,8 +44,8 @@ Feature: LMS.LTI component Scenario: Graded LTI component in LMS is correctly works Given the course has correct LTI credentials And the course has an LTI component with correct fields: - | open_in_a_new_page | weight | is_graded | - | False | 10 | True | + | open_in_a_new_page | weight | is_graded | has_score | + | False | 10 | True | True | And I submit answer to LTI question And I click on the "Progress" tab Then I see text "Problem Scores: 5/10" From 4d506016e7eab6c5707579b0e6888874e34c6504 Mon Sep 17 00:00:00 2001 From: Adam Palay Date: Wed, 4 Dec 2013 10:23:04 -0500 Subject: [PATCH 08/14] Add a backwards migration to delete the archive table --- .../0030_auto__chg_field_anonymoususerid_anonymous_user_id.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/djangoapps/student/migrations/0030_auto__chg_field_anonymoususerid_anonymous_user_id.py b/common/djangoapps/student/migrations/0030_auto__chg_field_anonymoususerid_anonymous_user_id.py index 3a7d53c47c..b096a9c322 100644 --- a/common/djangoapps/student/migrations/0030_auto__chg_field_anonymoususerid_anonymous_user_id.py +++ b/common/djangoapps/student/migrations/0030_auto__chg_field_anonymoususerid_anonymous_user_id.py @@ -27,6 +27,8 @@ class Migration(SchemaMigration): # Changing field 'AnonymousUserId.anonymous_user_id' db.alter_column('student_anonymoususerid', 'anonymous_user_id', self.gf('django.db.models.fields.CharField')(max_length=16, unique=True)) + db.execute("DROP TABLE student_anonymoususerid_temp_archive") + models = { 'auth.group': { 'Meta': {'object_name': 'Group'}, From e561c1354d2f9a550f2b27bb88d8e4d0f3f76203 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 4 Dec 2013 15:11:54 -0500 Subject: [PATCH 09/14] Add managemant command to generate sql to clean up tp truncated student ids in ORA db --- .../recover_truncated_anonymous_ids.py | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 common/djangoapps/student/management/commands/recover_truncated_anonymous_ids.py diff --git a/common/djangoapps/student/management/commands/recover_truncated_anonymous_ids.py b/common/djangoapps/student/management/commands/recover_truncated_anonymous_ids.py new file mode 100644 index 0000000000..09c5edce7a --- /dev/null +++ b/common/djangoapps/student/management/commands/recover_truncated_anonymous_ids.py @@ -0,0 +1,69 @@ +""" +Generate sql commands to fix truncated anonymous student ids in the ORA database +""" +import sys + +from django.core.management.base import NoArgsCommand + +from student.models import AnonymousUserId, anonymous_id_for_user + + +class Command(NoArgsCommand): + help = __doc__ + + def handle_noargs(self, **options): + """ + Reads a list of ids (newline separated) from stdin, and + dumps sql queries to run on the ORA database to fix those ids + from their truncated form to the full 32 character change. + + The following query will generate the list of ids needed to be fixed + from the ORA database: + + SELECT student_id FROM peer_grading_calibrationhistory WHERE LENGTH(student_id) = 16 + UNION SELECT student_id FROM controller_submission WHERE LENGTH(student_id) = 16 + UNION SELECT student_id FROM metrics_timing WHERE LENGTH(student_id) = 16 + UNION SELECT student_id FROM metrics_studentcourseprofile WHERE LENGTH(student_id) = 16 + UNION SELECT student_id FROM metrics_studentprofile WHERE LENGTH(student_id) = 16; + """ + + ids = [line.strip() for line in sys.stdin] + + old_ids = AnonymousUserId.objects.raw( + """ + SELECT * + FROM student_anonymoususerid_temp_archive + WHERE anonymous_user_id IN ({}) + """.format(','.join(['%s']*len(ids))), + ids + ) + + for old_id in old_ids: + new_id = anonymous_id_for_user(old_id.user, old_id.course_id) + + for table in ('peer_grading_calibrationhistory', 'controller_submission', 'metrics_timing'): + self.stdout.write( + "UPDATE {} " + "SET student_id = '{}' " + "WHERE student_id = '{}';\n".format( + table, + new_id, + old_id.anonymous_user_id, + ) + ) + + self.stdout.write( + "DELETE FROM metrics_studentcourseprofile " + "WHERE student_id = '{}' " + "AND problems_attempted = 0;\n".format(old_id.anonymous_user_id) + ) + + self.stdout.write( + "DELETE FROM metrics_studentprofile " + "WHERE student_id = '{}' " + "AND messages_sent = 0 " + "AND messages_received = 0 " + "AND average_message_feedback_length = 0 " + "AND student_is_staff_banned = 0 " + "AND student_cannot_submit_more_for_peer_grading = 0;\n".format(old_id.anonymous_user_id) + ) From b9926a2fd3d02c0a706ee329c781f30a949dfa75 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Wed, 4 Dec 2013 16:42:57 -0500 Subject: [PATCH 10/14] Add message explaining partial recovery --- lms/templates/instructor/staff_grading.html | 7 +++++++ .../open_ended_problems/combined_notifications.html | 7 +++++++ .../open_ended_problems/open_ended_flagged_problems.html | 7 +++++++ lms/templates/open_ended_problems/open_ended_problems.html | 7 +++++++ lms/templates/peer_grading/peer_grading.html | 7 +++++++ 5 files changed, 35 insertions(+) diff --git a/lms/templates/instructor/staff_grading.html b/lms/templates/instructor/staff_grading.html index c1710e7b89..0b37ff545b 100644 --- a/lms/templates/instructor/staff_grading.html +++ b/lms/templates/instructor/staff_grading.html @@ -30,6 +30,13 @@ "to restore their visibility. Rest assured, no student " "data has been lost.")} +
+ + ${_("We have recovered the majority of student responses. " + "Those responses submitted while the issue was active are " + "temporarily unavailable, but we are working on restoring " + "them, and they should be visible again shortly.")} +


diff --git a/lms/templates/open_ended_problems/combined_notifications.html b/lms/templates/open_ended_problems/combined_notifications.html index 12776b8759..675dd77567 100644 --- a/lms/templates/open_ended_problems/combined_notifications.html +++ b/lms/templates/open_ended_problems/combined_notifications.html @@ -24,6 +24,13 @@ "to restore their visibility. Rest assured, no student " "data has been lost.")} +
+ + ${_("We have recovered the majority of student responses. " + "Those responses submitted while the issue was active are " + "temporarily unavailable, but we are working on restoring " + "them, and they should be visible again shortly.")} +


diff --git a/lms/templates/open_ended_problems/open_ended_flagged_problems.html b/lms/templates/open_ended_problems/open_ended_flagged_problems.html index 4e77c2651c..7cc2cfefbe 100644 --- a/lms/templates/open_ended_problems/open_ended_flagged_problems.html +++ b/lms/templates/open_ended_problems/open_ended_flagged_problems.html @@ -27,6 +27,13 @@ "to restore their visibility. Rest assured, no student " "data has been lost.")} +
+ + ${_("We have recovered the majority of student responses. " + "Those responses submitted while the issue was active are " + "temporarily unavailable, but we are working on restoring " + "them, and they should be visible again shortly.")} +


diff --git a/lms/templates/open_ended_problems/open_ended_problems.html b/lms/templates/open_ended_problems/open_ended_problems.html index 2167e31ce8..893dbb57fa 100644 --- a/lms/templates/open_ended_problems/open_ended_problems.html +++ b/lms/templates/open_ended_problems/open_ended_problems.html @@ -24,6 +24,13 @@ "to restore their visibility. Rest assured, no student " "data has been lost.")} +
+ + ${_("We have recovered the majority of student responses. " + "Those responses submitted while the issue was active are " + "temporarily unavailable, but we are working on restoring " + "them, and they should be visible again shortly.")} +


diff --git a/lms/templates/peer_grading/peer_grading.html b/lms/templates/peer_grading/peer_grading.html index 3e2a421fcb..a04b2fc9a3 100644 --- a/lms/templates/peer_grading/peer_grading.html +++ b/lms/templates/peer_grading/peer_grading.html @@ -23,6 +23,13 @@ criteria.{end_li_tag} "to restore their visibility. Rest assured, no student " "data has been lost.")} +
+ + ${_("We have recovered the majority of student responses. " + "Those responses submitted while the issue was active are " + "temporarily unavailable, but we are working on restoring " + "them, and they should be visible again shortly.")} +


From a458b469d123930e357de20fc3ab32ec580e9fac Mon Sep 17 00:00:00 2001 From: Adam Palay Date: Wed, 4 Dec 2013 17:02:17 -0500 Subject: [PATCH 11/14] modify partial recovery message --- lms/templates/instructor/staff_grading.html | 9 +++++---- .../open_ended_problems/combined_notifications.html | 5 +++-- .../open_ended_problems/open_ended_flagged_problems.html | 5 +++-- .../open_ended_problems/open_ended_problems.html | 5 +++-- lms/templates/peer_grading/peer_grading.html | 5 +++-- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/lms/templates/instructor/staff_grading.html b/lms/templates/instructor/staff_grading.html index 0b37ff545b..209c4b00eb 100644 --- a/lms/templates/instructor/staff_grading.html +++ b/lms/templates/instructor/staff_grading.html @@ -32,10 +32,11 @@
- ${_("We have recovered the majority of student responses. " - "Those responses submitted while the issue was active are " - "temporarily unavailable, but we are working on restoring " - "them, and they should be visible again shortly.")} + ${_("Update: We have recovered the majority of student responses. " + "Those responses submitted while the issue was active " + "(approximately 2:00pm EST Dec 3 to 9:00am EST Dec 5) are " + "temporarily unavailable, but we are working on restoring " + "them, and they should be visible again shortly.")}
diff --git a/lms/templates/open_ended_problems/combined_notifications.html b/lms/templates/open_ended_problems/combined_notifications.html index 675dd77567..2f54b173c7 100644 --- a/lms/templates/open_ended_problems/combined_notifications.html +++ b/lms/templates/open_ended_problems/combined_notifications.html @@ -26,8 +26,9 @@
- ${_("We have recovered the majority of student responses. " - "Those responses submitted while the issue was active are " + ${_("Update: We have recovered the majority of student responses. " + "Those responses submitted while the issue was active " + "(approximately 2:00pm EST Dec 3 to 9:00am EST Dec 5) are " "temporarily unavailable, but we are working on restoring " "them, and they should be visible again shortly.")} diff --git a/lms/templates/open_ended_problems/open_ended_flagged_problems.html b/lms/templates/open_ended_problems/open_ended_flagged_problems.html index 7cc2cfefbe..9a43a9ff5c 100644 --- a/lms/templates/open_ended_problems/open_ended_flagged_problems.html +++ b/lms/templates/open_ended_problems/open_ended_flagged_problems.html @@ -29,8 +29,9 @@
- ${_("We have recovered the majority of student responses. " - "Those responses submitted while the issue was active are " + ${_("Update: We have recovered the majority of student responses. " + "Those responses submitted while the issue was active " + "(approximately 2:00pm EST Dec 3 to 9:00am EST Dec 5) are " "temporarily unavailable, but we are working on restoring " "them, and they should be visible again shortly.")} diff --git a/lms/templates/open_ended_problems/open_ended_problems.html b/lms/templates/open_ended_problems/open_ended_problems.html index 893dbb57fa..2a7c748556 100644 --- a/lms/templates/open_ended_problems/open_ended_problems.html +++ b/lms/templates/open_ended_problems/open_ended_problems.html @@ -26,8 +26,9 @@
- ${_("We have recovered the majority of student responses. " - "Those responses submitted while the issue was active are " + ${_("Update: We have recovered the majority of student responses. " + "Those responses submitted while the issue was active " + "(approximately 2:00pm EST Dec 3 to 9:00am EST Dec 5) are " "temporarily unavailable, but we are working on restoring " "them, and they should be visible again shortly.")} diff --git a/lms/templates/peer_grading/peer_grading.html b/lms/templates/peer_grading/peer_grading.html index a04b2fc9a3..ce8e495ff7 100644 --- a/lms/templates/peer_grading/peer_grading.html +++ b/lms/templates/peer_grading/peer_grading.html @@ -25,8 +25,9 @@ criteria.{end_li_tag}
- ${_("We have recovered the majority of student responses. " - "Those responses submitted while the issue was active are " + ${_("Update: We have recovered the majority of student responses. " + "Those responses submitted while the issue was active " + "(approximately 2:00pm EST Dec 3 to 9:00am EST Dec 5) are " "temporarily unavailable, but we are working on restoring " "them, and they should be visible again shortly.")} From 9d48bb0ef1459e8ca091c3e88e52d8f7567f26f6 Mon Sep 17 00:00:00 2001 From: Adam Palay Date: Thu, 5 Dec 2013 12:08:39 -0500 Subject: [PATCH 12/14] removes temporary error messages --- lms/templates/instructor/staff_grading.html | 19 ------------------- .../combined_notifications.html | 19 ------------------- .../open_ended_flagged_problems.html | 19 ------------------- .../open_ended_problems.html | 19 ------------------- lms/templates/peer_grading/peer_grading.html | 19 ------------------- 5 files changed, 95 deletions(-) diff --git a/lms/templates/instructor/staff_grading.html b/lms/templates/instructor/staff_grading.html index 209c4b00eb..59585308c1 100644 --- a/lms/templates/instructor/staff_grading.html +++ b/lms/templates/instructor/staff_grading.html @@ -22,25 +22,6 @@

${_("Staff grading")}

-
-
- - ${_("We're sorry, some student responses have been temporarily " - "hidden. We appreciate your patience as we work hard " - "to restore their visibility. Rest assured, no student " - "data has been lost.")} - -
- - ${_("Update: We have recovered the majority of student responses. " - "Those responses submitted while the issue was active " - "(approximately 2:00pm EST Dec 3 to 9:00am EST Dec 5) are " - "temporarily unavailable, but we are working on restoring " - "them, and they should be visible again shortly.")} - -
-
-
diff --git a/lms/templates/open_ended_problems/combined_notifications.html b/lms/templates/open_ended_problems/combined_notifications.html index 2f54b173c7..47bb01fc8e 100644 --- a/lms/templates/open_ended_problems/combined_notifications.html +++ b/lms/templates/open_ended_problems/combined_notifications.html @@ -16,25 +16,6 @@
${error_text}
-
-
- - ${_("We're sorry, some student responses have been temporarily " - "hidden. We appreciate your patience as we work hard " - "to restore their visibility. Rest assured, no student " - "data has been lost.")} - -
- - ${_("Update: We have recovered the majority of student responses. " - "Those responses submitted while the issue was active " - "(approximately 2:00pm EST Dec 3 to 9:00am EST Dec 5) are " - "temporarily unavailable, but we are working on restoring " - "them, and they should be visible again shortly.")} - -
-
-

${_("Open Ended Console")}

${_("Instructions")}

diff --git a/lms/templates/open_ended_problems/open_ended_flagged_problems.html b/lms/templates/open_ended_problems/open_ended_flagged_problems.html index 9a43a9ff5c..87d5a11bd8 100644 --- a/lms/templates/open_ended_problems/open_ended_flagged_problems.html +++ b/lms/templates/open_ended_problems/open_ended_flagged_problems.html @@ -19,25 +19,6 @@
${error_text}
-
-
- - ${_("We're sorry, some student responses have been temporarily " - "hidden. We appreciate your patience as we work hard " - "to restore their visibility. Rest assured, no student " - "data has been lost.")} - -
- - ${_("Update: We have recovered the majority of student responses. " - "Those responses submitted while the issue was active " - "(approximately 2:00pm EST Dec 3 to 9:00am EST Dec 5) are " - "temporarily unavailable, but we are working on restoring " - "them, and they should be visible again shortly.")} - -
-
-

${_("Flagged Open Ended Problems")}

${_("Instructions")}

diff --git a/lms/templates/open_ended_problems/open_ended_problems.html b/lms/templates/open_ended_problems/open_ended_problems.html index 2a7c748556..d5edc8edaf 100644 --- a/lms/templates/open_ended_problems/open_ended_problems.html +++ b/lms/templates/open_ended_problems/open_ended_problems.html @@ -16,25 +16,6 @@
${error_text}
-
-
- - ${_("We're sorry, some student responses have been temporarily " - "hidden. We appreciate your patience as we work hard " - "to restore their visibility. Rest assured, no student " - "data has been lost.")} - -
- - ${_("Update: We have recovered the majority of student responses. " - "Those responses submitted while the issue was active " - "(approximately 2:00pm EST Dec 3 to 9:00am EST Dec 5) are " - "temporarily unavailable, but we are working on restoring " - "them, and they should be visible again shortly.")} - -
-
-

${_("Open Ended Problems")}

${_("Instructions")}

${_("Here is a list of open ended problems for this course.")}

diff --git a/lms/templates/peer_grading/peer_grading.html b/lms/templates/peer_grading/peer_grading.html index ce8e495ff7..101e6341f5 100644 --- a/lms/templates/peer_grading/peer_grading.html +++ b/lms/templates/peer_grading/peer_grading.html @@ -15,25 +15,6 @@ criteria.{end_li_tag}
${error_text}
-
-
- - ${_("We're sorry, some student responses have been temporarily " - "hidden. We appreciate your patience as we work hard " - "to restore their visibility. Rest assured, no student " - "data has been lost.")} - -
- - ${_("Update: We have recovered the majority of student responses. " - "Those responses submitted while the issue was active " - "(approximately 2:00pm EST Dec 3 to 9:00am EST Dec 5) are " - "temporarily unavailable, but we are working on restoring " - "them, and they should be visible again shortly.")} - -
-
-

${_("Peer Grading")}

${_("Instructions")}

From 108e02e1ed75c7368139f5a65f5cd9890819ffdc Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Thu, 5 Dec 2013 14:41:30 -0500 Subject: [PATCH 13/14] Remove the one-time-use managemant command. --- .../recover_truncated_anonymous_ids.py | 69 ------------------- 1 file changed, 69 deletions(-) delete mode 100644 common/djangoapps/student/management/commands/recover_truncated_anonymous_ids.py diff --git a/common/djangoapps/student/management/commands/recover_truncated_anonymous_ids.py b/common/djangoapps/student/management/commands/recover_truncated_anonymous_ids.py deleted file mode 100644 index 09c5edce7a..0000000000 --- a/common/djangoapps/student/management/commands/recover_truncated_anonymous_ids.py +++ /dev/null @@ -1,69 +0,0 @@ -""" -Generate sql commands to fix truncated anonymous student ids in the ORA database -""" -import sys - -from django.core.management.base import NoArgsCommand - -from student.models import AnonymousUserId, anonymous_id_for_user - - -class Command(NoArgsCommand): - help = __doc__ - - def handle_noargs(self, **options): - """ - Reads a list of ids (newline separated) from stdin, and - dumps sql queries to run on the ORA database to fix those ids - from their truncated form to the full 32 character change. - - The following query will generate the list of ids needed to be fixed - from the ORA database: - - SELECT student_id FROM peer_grading_calibrationhistory WHERE LENGTH(student_id) = 16 - UNION SELECT student_id FROM controller_submission WHERE LENGTH(student_id) = 16 - UNION SELECT student_id FROM metrics_timing WHERE LENGTH(student_id) = 16 - UNION SELECT student_id FROM metrics_studentcourseprofile WHERE LENGTH(student_id) = 16 - UNION SELECT student_id FROM metrics_studentprofile WHERE LENGTH(student_id) = 16; - """ - - ids = [line.strip() for line in sys.stdin] - - old_ids = AnonymousUserId.objects.raw( - """ - SELECT * - FROM student_anonymoususerid_temp_archive - WHERE anonymous_user_id IN ({}) - """.format(','.join(['%s']*len(ids))), - ids - ) - - for old_id in old_ids: - new_id = anonymous_id_for_user(old_id.user, old_id.course_id) - - for table in ('peer_grading_calibrationhistory', 'controller_submission', 'metrics_timing'): - self.stdout.write( - "UPDATE {} " - "SET student_id = '{}' " - "WHERE student_id = '{}';\n".format( - table, - new_id, - old_id.anonymous_user_id, - ) - ) - - self.stdout.write( - "DELETE FROM metrics_studentcourseprofile " - "WHERE student_id = '{}' " - "AND problems_attempted = 0;\n".format(old_id.anonymous_user_id) - ) - - self.stdout.write( - "DELETE FROM metrics_studentprofile " - "WHERE student_id = '{}' " - "AND messages_sent = 0 " - "AND messages_received = 0 " - "AND average_message_feedback_length = 0 " - "AND student_is_staff_banned = 0 " - "AND student_cannot_submit_more_for_peer_grading = 0;\n".format(old_id.anonymous_user_id) - ) From be3ab1c5e9e3d3d4f4b087302ef8e544ee7f631b Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Thu, 5 Dec 2013 14:56:30 -0500 Subject: [PATCH 14/14] Drop the temp table --- ...op_student_anonymoususerid_temp_archive.py | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 common/djangoapps/student/migrations/0031_drop_student_anonymoususerid_temp_archive.py diff --git a/common/djangoapps/student/migrations/0031_drop_student_anonymoususerid_temp_archive.py b/common/djangoapps/student/migrations/0031_drop_student_anonymoususerid_temp_archive.py new file mode 100644 index 0000000000..ac7d0ed117 --- /dev/null +++ b/common/djangoapps/student/migrations/0031_drop_student_anonymoususerid_temp_archive.py @@ -0,0 +1,186 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + "Write your forwards methods here." + # Note: Remember to use orm['appname.ModelName'] rather than "from appname.models..." + db.execute("DROP TABLE student_anonymoususerid_temp_archive") + + def backwards(self, orm): + "Write your backwards methods here." + + 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'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + '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'}) + }, + '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.anonymoususerid': { + 'Meta': {'object_name': 'AnonymousUserId'}, + 'anonymous_user_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '32'}), + 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'student.courseenrollment': { + 'Meta': {'ordering': "('user', 'course_id')", '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'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'mode': ('django.db.models.fields.CharField', [], {'default': "'honor'", 'max_length': '100'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'student.courseenrollmentallowed': { + 'Meta': {'unique_together': "(('email', 'course_id'),)", 'object_name': 'CourseEnrollmentAllowed'}, + 'auto_enroll': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + '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'}), + 'email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + '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.testcenterregistration': { + 'Meta': {'object_name': 'TestCenterRegistration'}, + 'accommodation_code': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}), + 'accommodation_request': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}), + 'authorization_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_index': 'True'}), + 'client_authorization_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20', 'db_index': 'True'}), + 'confirmed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}), + 'course_id': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'eligibility_appointment_date_first': ('django.db.models.fields.DateField', [], {'db_index': 'True'}), + 'eligibility_appointment_date_last': ('django.db.models.fields.DateField', [], {'db_index': 'True'}), + 'exam_series_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'processed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}), + 'testcenter_user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['student.TestCenterUser']"}), + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'upload_error_message': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), + 'upload_status': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '20', 'blank': 'True'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}), + 'user_updated_at': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}) + }, + 'student.testcenteruser': { + 'Meta': {'object_name': 'TestCenterUser'}, + 'address_1': ('django.db.models.fields.CharField', [], {'max_length': '40'}), + 'address_2': ('django.db.models.fields.CharField', [], {'max_length': '40', 'blank': 'True'}), + 'address_3': ('django.db.models.fields.CharField', [], {'max_length': '40', 'blank': 'True'}), + 'candidate_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'db_index': 'True'}), + 'city': ('django.db.models.fields.CharField', [], {'max_length': '32', 'db_index': 'True'}), + 'client_candidate_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}), + 'company_name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}), + 'confirmed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}), + 'country': ('django.db.models.fields.CharField', [], {'max_length': '3', 'db_index': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'extension': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '8', 'blank': 'True'}), + 'fax': ('django.db.models.fields.CharField', [], {'max_length': '35', 'blank': 'True'}), + 'fax_country_code': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'db_index': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'middle_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'phone': ('django.db.models.fields.CharField', [], {'max_length': '35'}), + 'phone_country_code': ('django.db.models.fields.CharField', [], {'max_length': '3', 'db_index': 'True'}), + 'postal_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '16', 'blank': 'True'}), + 'processed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}), + 'salutation': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'state': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '20', 'blank': 'True'}), + 'suffix': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), + 'upload_error_message': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), + 'upload_status': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '20', 'blank': 'True'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['auth.User']", 'unique': 'True'}), + 'user_updated_at': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}) + }, + 'student.userprofile': { + 'Meta': {'object_name': 'UserProfile', 'db_table': "'auth_userprofile'"}, + 'allow_certificate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'courseware': ('django.db.models.fields.CharField', [], {'default': "'course.xml'", 'max_length': '255', '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.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}), + '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.userstanding': { + 'Meta': {'object_name': 'UserStanding'}, + 'account_status': ('django.db.models.fields.CharField', [], {'max_length': '31', 'blank': 'True'}), + 'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'standing_last_changed_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'standing'", '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'] + symmetrical = True