From c31fb7684dd8e398e11843efd4e40d98ea023b62 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Wed, 11 Jul 2012 15:08:10 -0400 Subject: [PATCH 1/5] Added utf8 conversion from stable branch. --- .../migrations/0007_convert_to_utf8.py | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 lms/djangoapps/student/migrations/0007_convert_to_utf8.py diff --git a/lms/djangoapps/student/migrations/0007_convert_to_utf8.py b/lms/djangoapps/student/migrations/0007_convert_to_utf8.py new file mode 100644 index 0000000000..21ca7bb7de --- /dev/null +++ b/lms/djangoapps/student/migrations/0007_convert_to_utf8.py @@ -0,0 +1,125 @@ +# encoding: 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): + db.execute_many(""" + ALTER DATABASE CHARACTER SET utf8 COLLATE utf8_general_ci; + ALTER TABLE certificates_generatedcertificate CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; + ALTER TABLE student_pendingemailchange CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; + ALTER TABLE student_pendingnamechange CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; + ALTER TABLE student_usertestgroup CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; + ALTER TABLE student_usertestgroup_users CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; + """) + + + def backwards(self, orm): + raise RuntimeError("Cannot reverse this migration.") + + + 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.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'"}, + 'courseware': ('django.db.models.fields.CharField', [], {'default': "'course.xml'", 'max_length': '255', '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', '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']"}) + }, + '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'] From c6f9f3ebe1c3a658d92d77d87c9265de913cec3d Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Wed, 11 Jul 2012 15:17:44 -0400 Subject: [PATCH 2/5] Renamed migrations since branch, making dev machine databases un-migratable. --- ...=> 0008_auto__del_courseregistration__add_courseenrollment.py} | 0 ...e_id.py => 0009_auto__chg_field_courseenrollment_course_id.py} | 0 ...eld_courseenrollment_user__del_unique_courseenrollment_use.py} | 0 ...eld_userprofile_gender__add_field_userprofile_date_of_birt.py} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename lms/djangoapps/student/migrations/{0007_auto__del_courseregistration__add_courseenrollment.py => 0008_auto__del_courseregistration__add_courseenrollment.py} (100%) rename lms/djangoapps/student/migrations/{0008_auto__chg_field_courseenrollment_course_id.py => 0009_auto__chg_field_courseenrollment_course_id.py} (100%) rename lms/djangoapps/student/migrations/{0009_auto__chg_field_courseenrollment_user__del_unique_courseenrollment_use.py => 0010_auto__chg_field_courseenrollment_user__del_unique_courseenrollment_use.py} (100%) rename lms/djangoapps/student/migrations/{0010_auto__add_field_userprofile_gender__add_field_userprofile_date_of_birt.py => 0011_auto__add_field_userprofile_gender__add_field_userprofile_date_of_birt.py} (100%) diff --git a/lms/djangoapps/student/migrations/0007_auto__del_courseregistration__add_courseenrollment.py b/lms/djangoapps/student/migrations/0008_auto__del_courseregistration__add_courseenrollment.py similarity index 100% rename from lms/djangoapps/student/migrations/0007_auto__del_courseregistration__add_courseenrollment.py rename to lms/djangoapps/student/migrations/0008_auto__del_courseregistration__add_courseenrollment.py diff --git a/lms/djangoapps/student/migrations/0008_auto__chg_field_courseenrollment_course_id.py b/lms/djangoapps/student/migrations/0009_auto__chg_field_courseenrollment_course_id.py similarity index 100% rename from lms/djangoapps/student/migrations/0008_auto__chg_field_courseenrollment_course_id.py rename to lms/djangoapps/student/migrations/0009_auto__chg_field_courseenrollment_course_id.py diff --git a/lms/djangoapps/student/migrations/0009_auto__chg_field_courseenrollment_user__del_unique_courseenrollment_use.py b/lms/djangoapps/student/migrations/0010_auto__chg_field_courseenrollment_user__del_unique_courseenrollment_use.py similarity index 100% rename from lms/djangoapps/student/migrations/0009_auto__chg_field_courseenrollment_user__del_unique_courseenrollment_use.py rename to lms/djangoapps/student/migrations/0010_auto__chg_field_courseenrollment_user__del_unique_courseenrollment_use.py diff --git a/lms/djangoapps/student/migrations/0010_auto__add_field_userprofile_gender__add_field_userprofile_date_of_birt.py b/lms/djangoapps/student/migrations/0011_auto__add_field_userprofile_gender__add_field_userprofile_date_of_birt.py similarity index 100% rename from lms/djangoapps/student/migrations/0010_auto__add_field_userprofile_gender__add_field_userprofile_date_of_birt.py rename to lms/djangoapps/student/migrations/0011_auto__add_field_userprofile_gender__add_field_userprofile_date_of_birt.py From d739e61ebd40fc3b20ee65ad3d6eee4e2f2cec0f Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Wed, 11 Jul 2012 16:33:46 -0400 Subject: [PATCH 3/5] Added check to make sure utf8 migration only runs on mysql. Had to add in certificates module. --- lms/djangoapps/certificates/__init__.py | 0 .../certificates/management/__init__.py | 0 .../management/commands/__init__.py | 0 .../management/commands/ungenerated_certs.py | 20 +++ .../0001_added_generatedcertificates.py | 93 +++++++++++ ...field_generatedcertificate_download_url.py | 91 +++++++++++ ..._add_field_generatedcertificate_enabled.py | 92 +++++++++++ ...icate_graded_certificate_id__add_field_.py | 108 +++++++++++++ ...to__add_field_generatedcertificate_name.py | 97 ++++++++++++ ...eld_generatedcertificate_certificate_id.py | 97 ++++++++++++ .../0007_auto__add_revokedcertificate.py | 122 ++++++++++++++ .../certificates/migrations/__init__.py | 0 lms/djangoapps/certificates/models.py | 144 +++++++++++++++++ lms/djangoapps/certificates/tests.py | 16 ++ lms/djangoapps/certificates/views.py | 149 ++++++++++++++++++ .../migrations/0007_convert_to_utf8.py | 19 +-- lms/envs/common.py | 1 + 17 files changed, 1040 insertions(+), 9 deletions(-) create mode 100644 lms/djangoapps/certificates/__init__.py create mode 100644 lms/djangoapps/certificates/management/__init__.py create mode 100644 lms/djangoapps/certificates/management/commands/__init__.py create mode 100644 lms/djangoapps/certificates/management/commands/ungenerated_certs.py create mode 100644 lms/djangoapps/certificates/migrations/0001_added_generatedcertificates.py create mode 100644 lms/djangoapps/certificates/migrations/0002_auto__add_field_generatedcertificate_download_url.py create mode 100644 lms/djangoapps/certificates/migrations/0003_auto__add_field_generatedcertificate_enabled.py create mode 100644 lms/djangoapps/certificates/migrations/0004_auto__add_field_generatedcertificate_graded_certificate_id__add_field_.py create mode 100644 lms/djangoapps/certificates/migrations/0005_auto__add_field_generatedcertificate_name.py create mode 100644 lms/djangoapps/certificates/migrations/0006_auto__chg_field_generatedcertificate_certificate_id.py create mode 100644 lms/djangoapps/certificates/migrations/0007_auto__add_revokedcertificate.py create mode 100644 lms/djangoapps/certificates/migrations/__init__.py create mode 100644 lms/djangoapps/certificates/models.py create mode 100644 lms/djangoapps/certificates/tests.py create mode 100644 lms/djangoapps/certificates/views.py diff --git a/lms/djangoapps/certificates/__init__.py b/lms/djangoapps/certificates/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lms/djangoapps/certificates/management/__init__.py b/lms/djangoapps/certificates/management/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lms/djangoapps/certificates/management/commands/__init__.py b/lms/djangoapps/certificates/management/commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lms/djangoapps/certificates/management/commands/ungenerated_certs.py b/lms/djangoapps/certificates/management/commands/ungenerated_certs.py new file mode 100644 index 0000000000..7755bb8f32 --- /dev/null +++ b/lms/djangoapps/certificates/management/commands/ungenerated_certs.py @@ -0,0 +1,20 @@ +from django.utils.simplejson import dumps +from django.core.management.base import BaseCommand, CommandError +from certificates.models import GeneratedCertificate +class Command(BaseCommand): + + help = """ + This command finds all GeneratedCertificate objects that do not have a + certificate generated. These come into being when a user requests a + certificate, or when grade_all_students is called (for pre-generating + certificates). + + It returns a json formatted list of users and their user ids + """ + + def handle(self, *args, **options): + users = GeneratedCertificate.objects.filter( + download_url = None ) + user_output = [{'user_id':user.user_id, 'name':user.name} + for user in users] + self.stdout.write(dumps(user_output) + "\n") diff --git a/lms/djangoapps/certificates/migrations/0001_added_generatedcertificates.py b/lms/djangoapps/certificates/migrations/0001_added_generatedcertificates.py new file mode 100644 index 0000000000..0dc76b31f8 --- /dev/null +++ b/lms/djangoapps/certificates/migrations/0001_added_generatedcertificates.py @@ -0,0 +1,93 @@ +# -*- 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): + # Adding model 'GeneratedCertificate' + db.create_table('certificates_generatedcertificate', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), + ('certificate_id', self.gf('django.db.models.fields.CharField')(max_length=32)), + )) + db.send_create_signal('certificates', ['GeneratedCertificate']) + + def backwards(self, orm): + # Deleting model 'GeneratedCertificate' + db.delete_table('certificates_generatedcertificate') + + 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'}) + }, + 'certificates.generatedcertificate': { + 'Meta': {'object_name': 'GeneratedCertificate'}, + 'certificate_id': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + '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'}) + } + } + + complete_apps = ['certificates'] \ No newline at end of file diff --git a/lms/djangoapps/certificates/migrations/0002_auto__add_field_generatedcertificate_download_url.py b/lms/djangoapps/certificates/migrations/0002_auto__add_field_generatedcertificate_download_url.py new file mode 100644 index 0000000000..ec1abd0154 --- /dev/null +++ b/lms/djangoapps/certificates/migrations/0002_auto__add_field_generatedcertificate_download_url.py @@ -0,0 +1,91 @@ +# -*- 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): + # Adding field 'GeneratedCertificate.download_url' + db.add_column('certificates_generatedcertificate', 'download_url', + self.gf('django.db.models.fields.CharField')(max_length=128, null=True), + keep_default=False) + + def backwards(self, orm): + # Deleting field 'GeneratedCertificate.download_url' + db.delete_column('certificates_generatedcertificate', 'download_url') + + 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'}) + }, + 'certificates.generatedcertificate': { + 'Meta': {'object_name': 'GeneratedCertificate'}, + 'certificate_id': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'download_url': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + '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'}) + } + } + + complete_apps = ['certificates'] \ No newline at end of file diff --git a/lms/djangoapps/certificates/migrations/0003_auto__add_field_generatedcertificate_enabled.py b/lms/djangoapps/certificates/migrations/0003_auto__add_field_generatedcertificate_enabled.py new file mode 100644 index 0000000000..880494d226 --- /dev/null +++ b/lms/djangoapps/certificates/migrations/0003_auto__add_field_generatedcertificate_enabled.py @@ -0,0 +1,92 @@ +# -*- 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): + # Adding field 'GeneratedCertificate.enabled' + db.add_column('certificates_generatedcertificate', 'enabled', + self.gf('django.db.models.fields.BooleanField')(default=False), + keep_default=False) + + def backwards(self, orm): + # Deleting field 'GeneratedCertificate.enabled' + db.delete_column('certificates_generatedcertificate', 'enabled') + + 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'}) + }, + 'certificates.generatedcertificate': { + 'Meta': {'object_name': 'GeneratedCertificate'}, + 'certificate_id': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'download_url': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + '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'}) + } + } + + complete_apps = ['certificates'] \ No newline at end of file diff --git a/lms/djangoapps/certificates/migrations/0004_auto__add_field_generatedcertificate_graded_certificate_id__add_field_.py b/lms/djangoapps/certificates/migrations/0004_auto__add_field_generatedcertificate_graded_certificate_id__add_field_.py new file mode 100644 index 0000000000..ffa6ec7175 --- /dev/null +++ b/lms/djangoapps/certificates/migrations/0004_auto__add_field_generatedcertificate_graded_certificate_id__add_field_.py @@ -0,0 +1,108 @@ +# encoding: 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): + + # Adding field 'GeneratedCertificate.graded_certificate_id' + db.add_column('certificates_generatedcertificate', 'graded_certificate_id', self.gf('django.db.models.fields.CharField')(max_length=32, null=True), keep_default=False) + + # Adding field 'GeneratedCertificate.graded_download_url' + db.add_column('certificates_generatedcertificate', 'graded_download_url', self.gf('django.db.models.fields.CharField')(max_length=128, null=True), keep_default=False) + + # Adding field 'GeneratedCertificate.grade' + db.add_column('certificates_generatedcertificate', 'grade', self.gf('django.db.models.fields.CharField')(max_length=5, null=True), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'GeneratedCertificate.graded_certificate_id' + db.delete_column('certificates_generatedcertificate', 'graded_certificate_id') + + # Deleting field 'GeneratedCertificate.graded_download_url' + db.delete_column('certificates_generatedcertificate', 'graded_download_url') + + # Deleting field 'GeneratedCertificate.grade' + db.delete_column('certificates_generatedcertificate', 'grade') + + + 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'}) + }, + 'certificates.generatedcertificate': { + 'Meta': {'object_name': 'GeneratedCertificate'}, + 'certificate_id': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'download_url': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'grade': ('django.db.models.fields.CharField', [], {'max_length': '5', 'null': 'True'}), + 'graded_certificate_id': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'graded_download_url': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + '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'}) + } + } + + complete_apps = ['certificates'] diff --git a/lms/djangoapps/certificates/migrations/0005_auto__add_field_generatedcertificate_name.py b/lms/djangoapps/certificates/migrations/0005_auto__add_field_generatedcertificate_name.py new file mode 100644 index 0000000000..c463145504 --- /dev/null +++ b/lms/djangoapps/certificates/migrations/0005_auto__add_field_generatedcertificate_name.py @@ -0,0 +1,97 @@ +# encoding: 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): + + # Adding field 'GeneratedCertificate.name' + db.add_column('certificates_generatedcertificate', 'name', self.gf('django.db.models.fields.CharField')(default='', max_length=255, blank=True), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'GeneratedCertificate.name' + db.delete_column('certificates_generatedcertificate', 'name') + + + 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'}) + }, + 'certificates.generatedcertificate': { + 'Meta': {'object_name': 'GeneratedCertificate'}, + 'certificate_id': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'download_url': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'grade': ('django.db.models.fields.CharField', [], {'max_length': '5', 'null': 'True'}), + 'graded_certificate_id': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}), + 'graded_download_url': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + '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'}) + } + } + + complete_apps = ['certificates'] diff --git a/lms/djangoapps/certificates/migrations/0006_auto__chg_field_generatedcertificate_certificate_id.py b/lms/djangoapps/certificates/migrations/0006_auto__chg_field_generatedcertificate_certificate_id.py new file mode 100644 index 0000000000..c046c4f901 --- /dev/null +++ b/lms/djangoapps/certificates/migrations/0006_auto__chg_field_generatedcertificate_certificate_id.py @@ -0,0 +1,97 @@ +# encoding: 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 'GeneratedCertificate.certificate_id' + db.alter_column('certificates_generatedcertificate', 'certificate_id', self.gf('django.db.models.fields.CharField')(max_length=32, null=True)) + + + def backwards(self, orm): + + # Changing field 'GeneratedCertificate.certificate_id' + db.alter_column('certificates_generatedcertificate', 'certificate_id', self.gf('django.db.models.fields.CharField')(default=None, max_length=32)) + + + 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'}) + }, + 'certificates.generatedcertificate': { + 'Meta': {'object_name': 'GeneratedCertificate'}, + 'certificate_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '32', 'null': 'True'}), + 'download_url': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'grade': ('django.db.models.fields.CharField', [], {'max_length': '5', 'null': 'True'}), + 'graded_certificate_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '32', 'null': 'True'}), + 'graded_download_url': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + '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'}) + } + } + + complete_apps = ['certificates'] diff --git a/lms/djangoapps/certificates/migrations/0007_auto__add_revokedcertificate.py b/lms/djangoapps/certificates/migrations/0007_auto__add_revokedcertificate.py new file mode 100644 index 0000000000..ee98eee990 --- /dev/null +++ b/lms/djangoapps/certificates/migrations/0007_auto__add_revokedcertificate.py @@ -0,0 +1,122 @@ +# encoding: 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): + + # Adding model 'RevokedCertificate' + db.create_table('certificates_revokedcertificate', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('explanation', self.gf('django.db.models.fields.TextField')(blank=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), + ('name', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)), + ('certificate_id', self.gf('django.db.models.fields.CharField')(default=None, max_length=32, null=True)), + ('graded_certificate_id', self.gf('django.db.models.fields.CharField')(default=None, max_length=32, null=True)), + ('download_url', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)), + ('graded_download_url', self.gf('django.db.models.fields.CharField')(max_length=128, null=True)), + ('grade', self.gf('django.db.models.fields.CharField')(max_length=5, null=True)), + ('enabled', self.gf('django.db.models.fields.BooleanField')(default=False)), + )) + db.send_create_signal('certificates', ['RevokedCertificate']) + + + def backwards(self, orm): + + # Deleting model 'RevokedCertificate' + db.delete_table('certificates_revokedcertificate') + + + 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'}) + }, + 'certificates.generatedcertificate': { + 'Meta': {'object_name': 'GeneratedCertificate'}, + 'certificate_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '32', 'null': 'True'}), + 'download_url': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'grade': ('django.db.models.fields.CharField', [], {'max_length': '5', 'null': 'True'}), + 'graded_certificate_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '32', 'null': 'True'}), + 'graded_download_url': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'certificates.revokedcertificate': { + 'Meta': {'object_name': 'RevokedCertificate'}, + 'certificate_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '32', 'null': 'True'}), + 'download_url': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'explanation': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'grade': ('django.db.models.fields.CharField', [], {'max_length': '5', 'null': 'True'}), + 'graded_certificate_id': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '32', 'null': 'True'}), + 'graded_download_url': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + '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'}) + } + } + + complete_apps = ['certificates'] diff --git a/lms/djangoapps/certificates/migrations/__init__.py b/lms/djangoapps/certificates/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lms/djangoapps/certificates/models.py b/lms/djangoapps/certificates/models.py new file mode 100644 index 0000000000..d3323eed77 --- /dev/null +++ b/lms/djangoapps/certificates/models.py @@ -0,0 +1,144 @@ +from django.conf import settings as settings + +from django.contrib.auth.models import User +from django.db import models + + +''' +Certificates are created for a student and an offering of a course. + +When a certificate is generated, a unique ID is generated so that +the certificate can be verified later. The ID is a UUID4, so that +it can't be easily guessed and so that it is unique. Even though +we save these generated certificates (for later verification), we +also record the UUID so that if we regenerate the certificate it +will have the same UUID. + +If certificates are being generated on the fly, a GeneratedCertificate +should be created with the user, certificate_id, and enabled set +when a student requests a certificate. When the certificate has been +generated, the download_url should be set. + +Certificates can also be pre-generated. In this case, the user, +certificate_id, and download_url are all set before the user does +anything. When the user requests the certificate, only enabled +needs to be set to true. + +''' + +class GeneratedCertificate(models.Model): + user = models.ForeignKey(User, db_index=True) + # This is the name at the time of request + name = models.CharField(blank=True, max_length=255) + + certificate_id = models.CharField(max_length=32, null=True, default=None) + graded_certificate_id = models.CharField(max_length=32, null=True, default=None) + + download_url = models.CharField(max_length=128, null=True) + graded_download_url = models.CharField(max_length=128, null=True) + + grade = models.CharField(max_length=5, null=True) + + # enabled should only be true if the student has earned a grade in the course + # The student must have a grade and request a certificate for enabled to be True + enabled = models.BooleanField(default=False) + +class RevokedCertificate(models.Model): + """ + This model is for when a GeneratedCertificate must be regenerated. This model + contains all the same fields, to store a record of what the GeneratedCertificate + was before it was revoked (at which time all of it's information can change when + it is regenerated). + + GeneratedCertificate may be deleted once they are revoked, and then created again. + For this reason, the only link between a GeneratedCertificate and RevokedCertificate + is that they share the same user. + """ + ####-------------------New Fields--------------------#### + explanation = models.TextField(blank=True) + + ####---------Fields from GeneratedCertificate---------#### + user = models.ForeignKey(User, db_index=True) + # This is the name at the time of request + name = models.CharField(blank=True, max_length=255) + + certificate_id = models.CharField(max_length=32, null=True, default=None) + graded_certificate_id = models.CharField(max_length=32, null=True, default=None) + + download_url = models.CharField(max_length=128, null=True) + graded_download_url = models.CharField(max_length=128, null=True) + + grade = models.CharField(max_length=5, null=True) + + enabled = models.BooleanField(default=False) + + +def revoke_certificate(certificate, explanation): + """ + This method takes a GeneratedCertificate. It records its information from the certificate + into a RevokedCertificate, and then marks the certificate as needing regenerating. + When the new certificiate is regenerated it will have new IDs and download URLS. + + Once this method has been called, it is safe to delete the certificate, or modify the + certificate's name or grade until it has been generated again. + """ + revoked = RevokedCertificate( user = certificate.user, + name = certificate.name, + certificate_id = certificate.certificate_id, + graded_certificate_id = certificate.graded_certificate_id, + download_url = certificate.download_url, + graded_download_url = certificate.graded_download_url, + grade = certificate.grade, + enabled = certificate.enabled) + + revoked.explanation = explanation + + certificate.certificate_id = None + certificate.graded_certificate_id = None + certificate.download_url = None + certificate.graded_download_url = None + + certificate.save() + revoked.save() + + + + +def certificate_state_for_student(student, grade): + ''' + This returns a dictionary with a key for state, and other information. The state is one of the + following: + + unavailable - A student is not eligible for a certificate. + requestable - A student is eligible to request a certificate + generating - A student has requested a certificate, but it is not generated yet. + downloadable - The certificate has been requested and is available for download. + + If the state is "downloadable", the dictionary also contains "download_url" and "graded_download_url". + + ''' + + if grade: + #TODO: Remove the following after debugging + if settings.DEBUG_SURVEY: + return {'state' : 'requestable' } + + try: + generated_certificate = GeneratedCertificate.objects.get(user = student) + if generated_certificate.enabled: + if generated_certificate.download_url: + return {'state' : 'downloadable', + 'download_url' : generated_certificate.download_url, + 'graded_download_url' : generated_certificate.graded_download_url} + else: + return {'state' : 'generating'} + else: + # If enabled=False, it may have been pre-generated but not yet requested + # Our output will be the same as if the GeneratedCertificate did not exist + pass + except GeneratedCertificate.DoesNotExist: + pass + return {'state' : 'requestable'} + else: + # No grade, no certificate. No exceptions + return {'state' : 'unavailable'} diff --git a/lms/djangoapps/certificates/tests.py b/lms/djangoapps/certificates/tests.py new file mode 100644 index 0000000000..501deb776c --- /dev/null +++ b/lms/djangoapps/certificates/tests.py @@ -0,0 +1,16 @@ +""" +This file demonstrates writing tests using the unittest module. These will pass +when you run "manage.py test". + +Replace this with more appropriate tests for your application. +""" + +from django.test import TestCase + + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.assertEqual(1 + 1, 2) diff --git a/lms/djangoapps/certificates/views.py b/lms/djangoapps/certificates/views.py new file mode 100644 index 0000000000..cf50c6b96e --- /dev/null +++ b/lms/djangoapps/certificates/views.py @@ -0,0 +1,149 @@ +import json +import logging +import settings +import uuid + +from django.conf import settings +from django.contrib.auth.decorators import login_required +from django.core.mail import send_mail +from django.http import Http404, HttpResponse +from django.shortcuts import redirect + +import courseware.grades as grades +from certificates.models import GeneratedCertificate, certificate_state_for_student, revoke_certificate +from mitxmako.shortcuts import render_to_response, render_to_string +from student.models import UserProfile +from student.survey_questions import exit_survey_list_for_student +from student.views import student_took_survey, record_exit_survey + +log = logging.getLogger("mitx.certificates") + +@login_required +def certificate_request(request): + ''' Attempt to send a certificate. ''' + if not settings.END_COURSE_ENABLED: + raise Http404 + + if request.method == "POST": + honor_code_verify = request.POST.get('cert_request_honor_code_verify', 'false') + name_verify = request.POST.get('cert_request_name_verify', 'false') + id_verify = request.POST.get('cert_request_id_verify', 'false') + error = '' + + def return_error(error): + return HttpResponse(json.dumps({'success':False, + 'error': error })) + + if honor_code_verify != 'true': + error += 'Please verify that you have followed the honor code to receive a certificate. ' + + if name_verify != 'true': + error += 'Please verify that your name is correct to receive a certificate. ' + + if id_verify != 'true': + error += 'Please certify that you understand the unique ID on the certificate. ' + + if len(error) > 0: + return return_error(error) + + survey_response = record_exit_survey(request, internal_request=True) + if not survey_response['success']: + return return_error( survey_response['error'] ) + + grade = None + student_gradesheet = grades.grade_sheet(request.user) + grade = student_gradesheet['grade'] + + if not grade: + return return_error('You have not earned a grade in this course. ') + + generate_certificate(request.user, grade) + + return HttpResponse(json.dumps({'success':True})) + + else: + #This is not a POST, we should render the page with the form + + grade_sheet = grades.grade_sheet(request.user) + certificate_state = certificate_state_for_student(request.user, grade_sheet['grade']) + + if certificate_state['state'] != "requestable": + return redirect("/profile") + + user_info = UserProfile.objects.get(user=request.user) + + took_survey = student_took_survey(user_info) + if settings.DEBUG_SURVEY: + took_survey = False + survey_list = [] + if not took_survey: + survey_list = exit_survey_list_for_student(request.user) + + + context = {'certificate_state' : certificate_state, + 'took_survey' : took_survey, + 'survey_list' : survey_list, + 'name' : user_info.name } + + + return render_to_response('cert_request.html', context) + + + +# This method should only be called if the user has a grade and has requested a certificate +def generate_certificate(user, grade): + # Make sure to see the comments in models.GeneratedCertificate to read about the valid + # states for a GeneratedCertificate object + if grade and user.is_active: + generated_certificate = None + + try: + generated_certificate = GeneratedCertificate.objects.get(user = user) + except GeneratedCertificate.DoesNotExist: + generated_certificate = GeneratedCertificate(user = user) + + generated_certificate.enabled = True + if generated_certificate.graded_download_url and (generated_certificate.grade != grade): + log.critical(u"A graded certificate has been pre-generated with the grade " + "of {gen_grade} but requested by user id {userid} with grade " + "{req_grade}! The download URLs were {graded_dl_url} and " + "{ungraded_dl_url}".format( + gen_grade=generated_certificate.grade, + req_grade=grade, + graded_dl_url=generated_certificate.graded_download_url, + ungraded_dl_url=generated_certificate.download_url, + userid=user.id)) + revoke_certificate(generated_certificate, "The grade on this certificate may be inaccurate.") + + user_name = UserProfile.objects.get(user = user).name + if generated_certificate.download_url and (generated_certificate.name != user_name): + log.critical(u"A Certificate has been pre-generated with the name of " + "{gen_name} but current name is {user_name} (user id is " + "{userid})! The download URLs were {graded_dl_url} and " + "{ungraded_dl_url}".format( + gen_name=generated_certificate.name.encode('utf-8'), + user_name=user_name.encode('utf-8'), + graded_dl_url=generated_certificate.graded_download_url, + ungraded_dl_url=generated_certificate.download_url, + userid=user.id)) + revoke_certificate(generated_certificate, "The name on this certificate may be inaccurate.") + + + generated_certificate.grade = grade + generated_certificate.name = user_name + generated_certificate.save() + + certificate_id = generated_certificate.certificate_id + + log.debug("Generating certificate for " + str(user.username) + " with ID: " + str(certificate_id)) + + # TODO: If the certificate was pre-generated, send the email that it is ready to download + if certificate_state_for_student(user, grade)['state'] == "downloadable": + subject = render_to_string('emails/certificate_ready_subject.txt',{}) + subject = ''.join(subject.splitlines()) + message = render_to_string('emails/certificate_ready.txt',{}) + + res=send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [user.email,]) + + else: + log.warning("Asked to generate a certificate for student " + str(user.username) + " but with a grade of " + str(grade) + " and active status " + str(user.is_active)) diff --git a/lms/djangoapps/student/migrations/0007_convert_to_utf8.py b/lms/djangoapps/student/migrations/0007_convert_to_utf8.py index 21ca7bb7de..84a3299c87 100644 --- a/lms/djangoapps/student/migrations/0007_convert_to_utf8.py +++ b/lms/djangoapps/student/migrations/0007_convert_to_utf8.py @@ -7,18 +7,19 @@ from django.db import models class Migration(SchemaMigration): def forwards(self, orm): - db.execute_many(""" - ALTER DATABASE CHARACTER SET utf8 COLLATE utf8_general_ci; - ALTER TABLE certificates_generatedcertificate CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; - ALTER TABLE student_pendingemailchange CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; - ALTER TABLE student_pendingnamechange CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; - ALTER TABLE student_usertestgroup CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; - ALTER TABLE student_usertestgroup_users CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; - """) + if db.backend_name == 'mysql': + db.execute_many(""" + ALTER DATABASE CHARACTER SET utf8 COLLATE utf8_general_ci; + ALTER TABLE student_pendingemailchange CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; + ALTER TABLE student_pendingnamechange CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; + ALTER TABLE student_usertestgroup CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; + ALTER TABLE student_usertestgroup_users CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; + """) def backwards(self, orm): - raise RuntimeError("Cannot reverse this migration.") + # Although this migration can't be undone, it is okay for it to be run backwards because it doesn't add/remove any fields + pass models = { diff --git a/lms/envs/common.py b/lms/envs/common.py index 5e026f2761..6035939441 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -365,6 +365,7 @@ INSTALLED_APPS = ( 'simplewiki', 'track', 'util', + 'certificates', # For testing 'django_jasmine', From 64888f1c1b42f9fdf214be26bcd7ad2585ce4654 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Thu, 12 Jul 2012 10:29:15 -0400 Subject: [PATCH 4/5] Re-added the migration to expand the meta field. --- ...rseregistration.py => 0008__auto__add_courseregistration.py} | 0 ... 0009_auto__del_courseregistration__add_courseenrollment.py} | 0 ...id.py => 0010_auto__chg_field_courseenrollment_course_id.py} | 0 ...d_courseenrollment_user__del_unique_courseenrollment_use.py} | 0 ...d_userprofile_gender__add_field_userprofile_date_of_birt.py} | 0 lms/djangoapps/student/models.py | 2 +- 6 files changed, 1 insertion(+), 1 deletion(-) rename lms/djangoapps/student/migrations/{0006_auto__add_courseregistration.py => 0008__auto__add_courseregistration.py} (100%) rename lms/djangoapps/student/migrations/{0008_auto__del_courseregistration__add_courseenrollment.py => 0009_auto__del_courseregistration__add_courseenrollment.py} (100%) rename lms/djangoapps/student/migrations/{0009_auto__chg_field_courseenrollment_course_id.py => 0010_auto__chg_field_courseenrollment_course_id.py} (100%) rename lms/djangoapps/student/migrations/{0010_auto__chg_field_courseenrollment_user__del_unique_courseenrollment_use.py => 0011_auto__chg_field_courseenrollment_user__del_unique_courseenrollment_use.py} (100%) rename lms/djangoapps/student/migrations/{0011_auto__add_field_userprofile_gender__add_field_userprofile_date_of_birt.py => 0012_auto__add_field_userprofile_gender__add_field_userprofile_date_of_birt.py} (100%) diff --git a/lms/djangoapps/student/migrations/0006_auto__add_courseregistration.py b/lms/djangoapps/student/migrations/0008__auto__add_courseregistration.py similarity index 100% rename from lms/djangoapps/student/migrations/0006_auto__add_courseregistration.py rename to lms/djangoapps/student/migrations/0008__auto__add_courseregistration.py diff --git a/lms/djangoapps/student/migrations/0008_auto__del_courseregistration__add_courseenrollment.py b/lms/djangoapps/student/migrations/0009_auto__del_courseregistration__add_courseenrollment.py similarity index 100% rename from lms/djangoapps/student/migrations/0008_auto__del_courseregistration__add_courseenrollment.py rename to lms/djangoapps/student/migrations/0009_auto__del_courseregistration__add_courseenrollment.py diff --git a/lms/djangoapps/student/migrations/0009_auto__chg_field_courseenrollment_course_id.py b/lms/djangoapps/student/migrations/0010_auto__chg_field_courseenrollment_course_id.py similarity index 100% rename from lms/djangoapps/student/migrations/0009_auto__chg_field_courseenrollment_course_id.py rename to lms/djangoapps/student/migrations/0010_auto__chg_field_courseenrollment_course_id.py diff --git a/lms/djangoapps/student/migrations/0010_auto__chg_field_courseenrollment_user__del_unique_courseenrollment_use.py b/lms/djangoapps/student/migrations/0011_auto__chg_field_courseenrollment_user__del_unique_courseenrollment_use.py similarity index 100% rename from lms/djangoapps/student/migrations/0010_auto__chg_field_courseenrollment_user__del_unique_courseenrollment_use.py rename to lms/djangoapps/student/migrations/0011_auto__chg_field_courseenrollment_user__del_unique_courseenrollment_use.py diff --git a/lms/djangoapps/student/migrations/0011_auto__add_field_userprofile_gender__add_field_userprofile_date_of_birt.py b/lms/djangoapps/student/migrations/0012_auto__add_field_userprofile_gender__add_field_userprofile_date_of_birt.py similarity index 100% rename from lms/djangoapps/student/migrations/0011_auto__add_field_userprofile_gender__add_field_userprofile_date_of_birt.py rename to lms/djangoapps/student/migrations/0012_auto__add_field_userprofile_gender__add_field_userprofile_date_of_birt.py diff --git a/lms/djangoapps/student/models.py b/lms/djangoapps/student/models.py index c5b0d444b9..81cf37c5fd 100644 --- a/lms/djangoapps/student/models.py +++ b/lms/djangoapps/student/models.py @@ -29,7 +29,7 @@ class UserProfile(models.Model): 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.CharField(blank=True, max_length=255) # JSON dictionary for future expansion + 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) From 2c2e2960340f525b6d81c2ce2a8ca36b27eaae93 Mon Sep 17 00:00:00 2001 From: Bridger Maxwell Date: Thu, 12 Jul 2012 10:32:53 -0400 Subject: [PATCH 5/5] Forgot to actually add migration file... --- .../migrations/0006_expand_meta_field.py | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 lms/djangoapps/student/migrations/0006_expand_meta_field.py diff --git a/lms/djangoapps/student/migrations/0006_expand_meta_field.py b/lms/djangoapps/student/migrations/0006_expand_meta_field.py new file mode 100644 index 0000000000..19fd402cef --- /dev/null +++ b/lms/djangoapps/student/migrations/0006_expand_meta_field.py @@ -0,0 +1,122 @@ +# encoding: 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.meta' + db.alter_column('auth_userprofile', 'meta', self.gf('django.db.models.fields.TextField')()) + + + def backwards(self, orm): + + # Changing field 'UserProfile.meta' + db.alter_column('auth_userprofile', 'meta', self.gf('django.db.models.fields.CharField')(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.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'"}, + 'courseware': ('django.db.models.fields.CharField', [], {'default': "'course.xml'", 'max_length': '255', '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', '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']"}) + }, + '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']