Commit 4022478f by Calen Pennington

Merge pull request #74 from MITx/certificates

Certificates
parents ae882214 549a51dd
# -*- 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
# -*- 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
# -*- 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
# 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']
# 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']
import 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)
graded_certificate_id = models.CharField(max_length=32, null=True)
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)
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 elligible for a certificate.
requestable - A student is elligible 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'}
"""
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)
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
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, certificate_id = uuid.uuid4().hex)
generated_certificate.enabled = True
if generated_certificate.graded_download_url and (generated_certificate.grade != grade):
log.critical("A graded certificate has been pre-generated with the grade of " + str(generated_certificate.grade) + " but requested with grade " + str(grade) + \
"! The download URL is " + str(generated_certificate.graded_download_url))
user_name = UserProfile.objects.get(user = user).name
if generated_certificate.download_url and (generated_certificate.name != user_name):
log.critical("A Certificate has been pre-generated with the name of " + str(generated_certificate.name) + " but current name is " + str(user_name) + \
"! The download URL is " + str(generated_certificate.download_url))
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: " + 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))
"""
Course settings module. The settings are based of django.conf. All settings in
courseware.global_course_settings are first applied, and then any settings
in the settings.DATA_DIR/course_settings.py are applied. A setting must be
in ALL_CAPS.
Settings are used by calling
from courseware import course_settings
Note that courseware.course_settings is not a module -- it's an object. So
importing individual settings is not possible:
from courseware.course_settings import GRADER # This won't work.
"""
import imp
import logging
import sys
import types
from django.conf import settings
from courseware import global_course_settings
from courseware import graders
_log = logging.getLogger("mitx.courseware")
class Settings(object):
def __init__(self):
# update this dict from global settings (but only for ALL_CAPS settings)
for setting in dir(global_course_settings):
if setting == setting.upper():
setattr(self, setting, getattr(global_course_settings, setting))
data_dir = settings.DATA_DIR
fp = None
try:
fp, pathname, description = imp.find_module("course_settings", [data_dir])
mod = imp.load_module("course_settings", fp, pathname, description)
except Exception as e:
_log.exception("Unable to import course settings file from " + data_dir + ". Error: " + str(e))
mod = types.ModuleType('course_settings')
finally:
if fp:
fp.close()
for setting in dir(mod):
if setting == setting.upper():
setting_value = getattr(mod, setting)
setattr(self, setting, setting_value)
# Here is where we should parse any configurations, so that we can fail early
self.GRADER = graders.grader_from_conf(self.GRADER)
course_settings = Settings()
\ No newline at end of file
GRADER = [
{
'type' : "Homework",
'min_count' : 12,
'drop_count' : 2,
'short_label' : "HW",
'weight' : 0.15,
},
{
'type' : "Lab",
'min_count' : 12,
'drop_count' : 2,
'category' : "Labs",
'weight' : 0.15
},
{
'type' : "Midterm",
'name' : "Midterm Exam",
'short_label' : "Midterm",
'weight' : 0.3,
},
{
'type' : "Final",
'name' : "Final Exam",
'short_label' : "Final",
'weight' : 0.4,
}
]
GRADE_CUTOFFS = {'A' : 0.87, 'B' : 0.7, 'C' : 0.6}
import json
import logging
import os
import random
......@@ -17,12 +16,16 @@ from mitxmako.shortcuts import render_to_response, render_to_string
#from django.views.decorators.csrf import ensure_csrf_cookie
from django.db import connection
from django.views.decorators.cache import cache_control
from django_future.csrf import ensure_csrf_cookie
from lxml import etree
from courseware import course_settings
from module_render import render_module, modx_dispatch
from certificates.models import GeneratedCertificate, certificate_state_for_student
from models import StudentModule
from student.models import UserProfile
from student.views import student_took_survey
import courseware.content_parser as content_parser
import courseware.modules.capa_module
......@@ -45,10 +48,13 @@ def gradebook(request):
'id' : s.id,
'email': s.email,
'grade_info' : grades.grade_sheet(s),
'realname' : UserProfile.objects.get(user = s).name
'realname' : UserProfile.objects.get(user = s).name,
} for s in student_objects]
return render_to_response('gradebook.html',{'students':student_info})
return render_to_response('gradebook.html',
{'students':student_info,
'grade_cutoffs' : course_settings.GRADE_CUTOFFS,}
)
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
def profile(request, student_id = None):
......@@ -66,16 +72,30 @@ def profile(request, student_id = None):
student = User.objects.get( id = int(student_id))
user_info = UserProfile.objects.get(user=student) # request.user.profile_cache #
grade_sheet = grades.grade_sheet(student)
context={'name':user_info.name,
'username':student.username,
'location':user_info.location,
'language':user_info.language,
'email':student.email,
'format_url_params' : content_parser.format_url_params,
'csrf':csrf(request)['csrf_token']
'csrf':csrf(request)['csrf_token'],
'grade_cutoffs' : course_settings.GRADE_CUTOFFS,
'grade_sheet' : grade_sheet,
}
context.update(grades.grade_sheet(student))
if settings.END_COURSE_ENABLED:
took_survey = student_took_survey(user_info)
if settings.DEBUG_SURVEY:
took_survey = False
certificate_state = certificate_state_for_student(student, grade_sheet['grade'])
context.update({'certificate_state' : certificate_state,
'took_survey' : took_survey})
return render_to_response('profile.html', context)
......
......@@ -6,11 +6,28 @@ Replace this with more appropriate tests for your application.
"""
from django.test import TestCase
from survey_questions import exit_survey_questions
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)
class ExitSurveyTest(TestCase):
def test_unique_names(self):
question_names = set()
for question in exit_survey_questions['common_questions'] + exit_survey_questions['random_questions']:
name = question['question_name']
self.assertFalse( name in question_names, "There is a duplicate of name " + name )
question_names.add(name)
def test_question_format(self):
for question in exit_survey_questions['common_questions'] + exit_survey_questions['random_questions']:
self.assertTrue( 'question_name' in question, "All questions need a question_name. Failed on: " + str(question) )
self.assertTrue( 'label' in question, "All questions need a label. Failed on: " + str(question) )
question_type = question['type']
if question_type == 'checkbox' or question_type == 'short_field' or question_type == 'medium_field':
# No other required fields
pass
elif question_type == 'radio' or question_type == 'select_many':
self.assertTrue( 'choices' in question, "All radio/select_many questions need choices. Failed on: " + str(question) )
else:
self.assertTrue(False, "Found illegal question type. Failed on: " + str(question) )
......@@ -8,6 +8,7 @@ import uuid
from django.conf import settings
from django.contrib.auth import logout, authenticate, login
from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import PasswordResetForm
from django.contrib.auth.models import User
from django.core.context_processors import csrf
......@@ -21,6 +22,7 @@ from mako import exceptions
from django_future.csrf import ensure_csrf_cookie
from student.survey_questions import exit_survey_list_for_student
from models import Registration, UserProfile, PendingNameChange, PendingEmailChange
log = logging.getLogger("mitx.student")
......@@ -456,3 +458,67 @@ def accept_name_change(request):
pnc.delete()
return HttpResponse(json.dumps({'success':True}))
@login_required
def record_exit_survey(request, internal_request = False):
if not settings.END_COURSE_ENABLED:
raise Http404
if request.method == "POST":
# If internal_request = True, this is a survey that was submitted with another form
# We will record it and return the results as a dictionary, instead of a json HttpResponse
def returnResults(return_data):
if internal_request:
return return_data
else:
return HttpResponse(json.dumps(return_data))
if 'survey_results' not in request.POST:
if internal_request:
return returnResults({'success':True})
else:
return returnResults({'success':False, 'error':'There was a problem receiving your survey response.'})
response = json.loads(request.POST['survey_results'])
up = UserProfile.objects.get(user=request.user)
meta = up.get_meta()
if '6002x_exit_response' in meta:
# Once we got a response, we don't show them the survey form again, so this is a really odd case anyway
log.warning("Received an extra survey response. " + request.POST['survey_results'])
return returnResults({'success':True})
else:
if internal_request and all( value == None or value == [''] for value in response.values() ):
# If all values are empty, then the student didn't answer any questions.
# In the case of this being an internal request, we don't mark the survey as taken
return returnResults({'success':True})
meta['6002x_exit_response'] = response
up.set_meta(meta)
up.save()
return returnResults({'success':True})
else:
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 = {'took_survey' : took_survey,
'survey_list' : survey_list}
return render_to_response('exit_survey.html', context)
def student_took_survey(userprofile):
meta = userprofile.get_meta()
return '6002x_exit_response' in meta
......@@ -27,13 +27,15 @@ def render_to_string(template_name, dictionary, context=None, namespace='main'):
# collapse context_instance to a single dictionary for mako
context_dictionary = {}
context_instance['settings'] = settings
for d in mitxmako.middleware.requestcontext:
context_dictionary.update(d)
if mitxmako.middleware.requestcontext:
for d in mitxmako.middleware.requestcontext:
context_dictionary.update(d)
for d in context_instance:
context_dictionary.update(d)
if context:
context_dictionary.update(context)
# fetch and render template
print namespace, template_name
template = middleware.lookup[namespace].get_template(template_name)
return template.render(**context_dictionary)
......
......@@ -5,6 +5,12 @@ import tempfile
import djcelery
# Enables certificate requests and the exit survey
END_COURSE_ENABLED = False
# If enabled, always shows all survey questions. Has no effect without END_COURSE_ENABLED
DEBUG_SURVEY = False
# from settings2.askbotsettings import LIVESETTINGS_OPTIONS
DEFAULT_GROUPS = []
......@@ -144,6 +150,7 @@ INSTALLED_APPS = (
'simplewiki',
'track',
'circuit',
'certificates',
'perfstats',
'util',
# Uncomment the next line to enable the admin:
......
This source diff could not be displayed because it is too large. You can view the blob instead.
/* line 1, sass/marketing-ie.scss */
body {
margin: 0;
padding: 0; }
/* line 6, sass/marketing-ie.scss */
.wrapper, .subpage, section.copyright, section.tos, section.privacy-policy, section.honor-code, header.announcement div, section.index-content, footer {
margin: 0;
overflow: hidden; }
/* line 12, sass/marketing-ie.scss */
div#enroll form {
display: none; }
body{margin:0;padding:0}.wrapper,.subpage,section.copyright,section.tos,section.privacy-policy,section.honor-code,header.announcement div,section.index-content,footer{margin:0;overflow:hidden}div#enroll form{display:none}
$(function() {
//This is used to scroll error divs to the user's attention. It can be removed when we switch to the branch with jQuery.ScrollTo
jQuery.fn.scrollMinimal = function(smooth) {
var cTop = this.offset().top;
var cHeight = this.outerHeight(true);
var windowTop = $(window).scrollTop();
var visibleHeight = $(window).height();
if (cTop < windowTop) {
$('body').animate({
'scrollTop': cTop
}, 'slow', 'swing');
} else if (cTop + cHeight > windowTop + visibleHeight) {
$('body').animate({
'scrollTop': cTop - visibleHeight + cHeight
}, 'slow', 'swing');
}
};
var get_survey_data = function() {
var values = {};
//First we set the value of every input to null. This assures that even
//if a checkbox isn't checked or a question isn't answered, its name is
//still in the dictionary so we know the question was on the form.
var inputs = $("#survey_fieldset :input");
inputs.each(function(index, element) {
values[element.getAttribute("name")] = null;
});
//Grab the values from the survey_fieldset
var serializedArray = inputs.serializeArray();
for (var i = 0; i < serializedArray.length; i++) {
var key = serializedArray[i]['name'];
var value = serializedArray[i]['value'];
if (key in values && values[key] != null) {
values[key].push(value);
} else {
values[key] = [value];
}
}
return JSON.stringify(values);
};
$("#cert_request_form").submit(function() {
var values = {
'cert_request_honor_code_verify': $("#cert_request_honor_code_verify").is(':checked'),
'cert_request_name_verify': $("#cert_request_name_verify").is(':checked'),
'cert_request_id_verify': $("#cert_request_id_verify").is(':checked'),
//Notice that if the survey is present, it's data is in here! That is important
'survey_results': get_survey_data(),
};
postJSON('/certificate_request', values, function(data) {
if (data.success) {
$("#cert_request").html("<h1>Certificate Request Received</h1><p>Thank you! We will let you know when the certificate is ready to download from the <a href='/profile'>Profile page</a>.</p>");
} else {
$("#cert_request_error").html(data.error).scrollMinimal();
}
});
log_event("certificate_request", values);
return false;
});
$("#survey_form").submit(function() {
var values = { 'survey_results': get_survey_data() };
postJSON('/exit_survey', values, function(data) {
if (data.success) {
$("#survey").html("<h1>Survey Response Recorded</h1><p>Thank you for filling out the survey! You can now return to the <a href='/profile'>Profile page</a>.</p>");
} else {
$("#survey_error").html(data.error).scrollMinimal();
}
});
log_event("exit_survey_submission", values);
return false;
});
});
<%inherit file="main.html" />
<%namespace name="survey_fields" file="survey_fields.html"/>
<%block name="title"><title>Certificate Request - MITx 6.002x</title></%block>
<%block name="headextra">
<script type="text/javascript" src="${ settings.LIB_URL }certificate_survey.js"></script>
</%block>
<%include file="navigation.html" args="active_page=''" />
<%block name="bodyclass">cert-req</%block>
<section class="main-content">
<div id="cert_request">
<h1>You can request a certificate which verifies that you passed this course. When the certificate has been generated, you will receive an email notification that it can be downloaded from your Profile page.</h1>
<p>The certificate will indicate that <strong>${name}</strong> has successfully completed the Fall 2012 offering of <strong>Circuits and Electronics 6.002x</strong>. Please ensure that this information is correct.</p>
<form id="cert_request_form">
<fieldset id="cert_request_fieldset">
<div id="cert_request_error"> </div>
<ul>
<li class="verify">
<input type="checkbox" id="cert_request_honor_code_verify"/>
<label> I certify that I have not violated the Honor Code for this course. </label>
</li>
<li class="verify">
<input type="checkbox" id="cert_request_name_verify"/>
<label> The name shown above is correct. </label>
</li>
<li class="verify">
<input type="checkbox" id="cert_request_id_verify"/>
<label> I understand that my certificate contains my name a unique ID which will be visible on the certificate. I will give this ID only to those who I wish to visit the verification page on www.edXOnline.org to confirm that I passed Circuits and Electronics 6.002x. </label>
</li>
</ul>
</fieldset>
%if not took_survey:
<fieldset id="survey_fieldset">
<h1>Thank you for taking 6.002x! You could help us a lot by filling out this (optional) survey.</h1>
<p>This survey is collected only for research purposes. It is completely optional and will not affect your grade.</p>
${survey_fields.body(survey_list)}
</fieldset>
%endif
<input type="submit" id="" value="Generate Certificate" />
</form>
</div>
</section>
Congratulations on completing 6.002x! We have received your request
for a certifcate, and it is ready to download. Please visit your
<a href="http://6002x.mitx.mit.edu/Profile">Profile</a> to download your certificate.
\ No newline at end of file
6.002x Certificate Ready to Download
\ No newline at end of file
<%inherit file="main.html" />
<%namespace name="survey_fields" file="survey_fields.html"/>
<%block name="title"><title>Survey - MITx 6.002x</title></%block>
<%block name="headextra">
<script type="text/javascript" src="${ settings.LIB_URL }certificate_survey.js"></script>
</%block>
<%block name="bodyclass">exit-survey</%block>
<%include file="navigation.html" args="active_page=''" />
<section id="survey" class="main-content">
<form id="survey_form">
%if not took_survey:
<fieldset id="survey_fieldset">
<h1>Thank you for taking 6.002x! You could help us a lot by filling out this (optional) survey.</h1>
<p>This survey is collected only for research purposes. It is completely optional and will not affect your grade.</p>
${survey_fields.body(survey_list)}
</fieldset>
%else:
<h1>You have already taken the survey.</h1>
<p>Thank you for your help! You can only take the survey once, and we already have your submission.</p>
%endif
<input type="submit" id="" value="Submit Survey" />
</form>
</section>
......@@ -6,10 +6,11 @@
<script type="text/javascript" src="/static/js/flot/jquery.flot.symbol.js"></script>
<style type="text/css">
.grade_a {color:green;}
.grade_b {color:Chocolate;}
.grade_c {color:DimGray;}
.grade_none {color:LightGray;}
.grade_A {color:green;}
.grade_B {color:Chocolate;}
.grade_C {color:DarkSlateGray;}
.grade_F {color:DimGray;}
.grade_None {color:LightGray;}
</style>
</%block>
......@@ -29,27 +30,23 @@
<tr> <!-- Header Row -->
<th>Student</th>
%for section in templateSummary:
%if 'subscores' in section:
%for subsection in section['subscores']:
<th>${subsection['label']}</th>
%endfor
<th>${section['totallabel']}</th>
%else:
<th>${section['category']}</th>
%endif
%for section in templateSummary['section_breakdown']:
<th>${section['label']}</th>
%endfor
<th>Total</th>
</tr>
<%def name="percent_data(percentage)">
<%
data_class = "grade_none"
if percentage > .87:
data_class = "grade_a"
elif percentage > .70:
data_class = "grade_b"
elif percentage > .6:
data_class = "grade_c"
letter_grade = 'None'
if percentage > 0:
letter_grade = 'F'
for grade in ['A', 'B', 'C']:
if percentage >= grade_cutoffs[grade]:
letter_grade = grade
break
data_class = "grade_" + letter_grade
%>
<td class="${data_class}">${ "{0:.0%}".format( percentage ) }</td>
</%def>
......@@ -57,16 +54,10 @@
%for student in students:
<tr>
<td><a href="/profile/${student['id']}/">${student['username']}</a></td>
%for section in student['grade_info']['grade_summary']:
%if 'subscores' in section:
%for subsection in section['subscores']:
${percent_data( subsection['percentage'] )}
%endfor
${percent_data( section['totalscore'] )}
%else:
${percent_data( section['totalscore'] )}
%endif
%for section in student['grade_info']['grade_summary']['section_breakdown']:
${percent_data( section['percent'])}
%endfor
<th>${percent_data( student['grade_info']['grade_summary']['percent'])}</th>
</tr>
%endfor
</table>
......
<%inherit file="main.html" />
<%namespace name="profile_graphs" file="profile_graphs.js"/>
<%namespace name="survey_fields" file="survey_fields.html"/>
<%block name="title"><title>Profile - MITx 6.002x</title></%block>
<%!
from django.core.urlresolvers import reverse
from django.conf import settings
%>
<%block name="headextra">
......@@ -12,7 +14,7 @@
<script type="text/javascript" src="/static/js/flot/jquery.flot.stack.js"></script>
<script type="text/javascript" src="/static/js/flot/jquery.flot.symbol.js"></script>
<script>
${profile_graphs.body(grade_summary, "grade-detail-graph")}
${profile_graphs.body(grade_sheet['grade_summary'], grade_cutoffs, "grade-detail-graph")}
</script>
<script>
......@@ -100,6 +102,7 @@ $(function() {
"new_email":new_email});
return false;
});
$("#change_name_form").submit(function(){
var new_name = $('#new_name_field').val();
......@@ -119,7 +122,6 @@ $(function() {
"rationale":rationale});
return false;
});
});
</script>
</%block>
......@@ -135,10 +137,42 @@ $(function() {
<h1>Course Progress</h1>
</header>
%if settings.END_COURSE_ENABLED:
<div id="grade">
%if grade_sheet['grade']:
<p>Final Grade: <strong>${grade_sheet['grade']}</strong></p>
%if certificate_state['state'] == "requestable":
<a class="cert_request_link" href="/certificate_request">Request Certificate</a>
%elif certificate_state['state'] == "downloadable":
%if certificate_state['download_url']:
<a href="${certificate_state['download_url']}" target="_blank">Download Certificate</a>
%endif
%if certificate_state['graded_download_url']:
<a href="${certificate_state['graded_download_url']}" target="_blank">Download Certificate (with Grade)</a>
%endif
%elif certificate_state['state'] == "generating":
<a href="#">Certificate is being generated...</a>
%else:
<a href="#">No certificate available</a>
%endif
%else:
<p> No letter grade has been earned for this course </p>
%endif
%if not took_survey:
<a class="survey_link" href="/exit_survey">Take the survey</a>
%endif
</div>
%endif
<div id="grade-detail-graph"></div>
<ol class="chapters">
%for chapter in courseware_summary:
%for chapter in grade_sheet['courseware_summary']:
%if not chapter['chapter'] == "hidden":
<li>
<h2><a href="${reverse('courseware_chapter', args=format_url_params([chapter['course'], chapter['chapter']])) }">
......@@ -150,11 +184,11 @@ $(function() {
<%
earned = section['section_total'].earned
total = section['section_total'].possible
percentageString = "{0:.0%}".format( float(earned)/total) if earned > 0 else ""
percentageString = "{0:.0%}".format( float(earned)/total) if earned > 0 and total > 0 else ""
%>
<h3><a href="${reverse('courseware_section', args=format_url_params([chapter['course'], chapter['chapter'], section['section']])) }">
${ section['section'] }</a> ${"({0:g}/{1:g}) {2}".format( earned, total, percentageString )}</h3>
${ section['section'] }</a> ${"({0:.3n}/{1:.3n}) {2}".format( float(earned), float(total), percentageString )}</h3>
${section['subtitle']}
%if 'due' in section and section['due']!="":
due ${section['due']}
......@@ -164,7 +198,7 @@ $(function() {
<ol class="scores">
${ "Problem Scores: " if section['graded'] else "Practice Scores: "}
%for score in section['scores']:
<li class="score">${"{0:g}/{1:g}".format(score.earned,score.possible)}</li>
<li class="score">${"{0:.3n}/{1:.3n}".format(float(score.earned),float(score.possible))}</li>
%endfor
</ol>
%endif
......
<%page args="grade_summary, graph_div_id, **kwargs"/>
<%page args="grade_summary, grade_cutoffs, graph_div_id, **kwargs"/>
<%!
import json
import math
%>
$(function () {
......@@ -9,7 +10,7 @@ $(function () {
position: 'absolute',
display: 'none',
top: y + 5,
left: x + 5,
left: x + 15,
border: '1px solid #000',
padding: '4px 6px',
color: '#fff',
......@@ -19,100 +20,94 @@ $(function () {
}
/* -------------------------------- Grade detail bars -------------------------------- */
<%
colors = ["#b72121", "#600101", "#666666", "#333333"]
categories = {}
tickIndex = 1
sectionSpacer = 0.5
sectionSpacer = 0.25
sectionIndex = 0
series = []
ticks = [] #These are the indices and x-axis labels for the data
bottomTicks = [] #Labels on the bottom
detail_tooltips = {} #This an dictionary mapping from 'section' -> array of detail_tooltips
droppedScores = [] #These are the datapoints to indicate assignments which aren't factored into the total score
droppedScores = [] #These are the datapoints to indicate assignments which are not factored into the total score
dropped_score_tooltips = []
for section in grade_summary:
if 'subscores' in section: ##This is for sections like labs or homeworks, with several smaller components and a total
series.append({
'label' : section['category'],
'data' : [[i + tickIndex, score['percentage']] for i,score in enumerate(section['subscores'])],
'color' : colors[sectionIndex]
})
ticks += [[i + tickIndex, score['label'] ] for i,score in enumerate(section['subscores'])]
bottomTicks.append( [tickIndex + len(section['subscores'])/2, section['category']] )
detail_tooltips[ section['category'] ] = [score['summary'] for score in section['subscores']]
droppedScores += [[tickIndex + index, 0.05] for index in section['dropped_indices']]
dropExplanation = "The lowest {0} {1} scores are dropped".format( len(section['dropped_indices']), section['category'] )
dropped_score_tooltips += [dropExplanation] * len(section['dropped_indices'])
tickIndex += len(section['subscores']) + sectionSpacer
category_total_label = section['category'] + " Total"
series.append({
'label' : category_total_label,
'data' : [ [tickIndex, section['totalscore']] ],
'color' : colors[sectionIndex]
})
ticks.append( [tickIndex, section['totallabel']] )
detail_tooltips[category_total_label] = [section['totalscore_summary']]
else:
series.append({
'label' : section['category'],
'data' : [ [tickIndex, section['totalscore']] ],
'color' : colors[sectionIndex]
})
ticks.append( [tickIndex, section['totallabel']] )
detail_tooltips[section['category']] = [section['totalscore_summary']]
for section in grade_summary['section_breakdown']:
if section.get('prominent', False):
tickIndex += sectionSpacer
if section['category'] not in categories:
colorIndex = len(categories) % len(colors)
categories[ section['category'] ] = {'label' : section['category'],
'data' : [],
'color' : colors[colorIndex]}
categoryData = categories[ section['category'] ]
categoryData['data'].append( [tickIndex, section['percent']] )
ticks.append( [tickIndex, section['label'] ] )
if section['category'] in detail_tooltips:
detail_tooltips[ section['category'] ].append( section['detail'] )
else:
detail_tooltips[ section['category'] ] = [ section['detail'], ]
if 'mark' in section:
droppedScores.append( [tickIndex, 0.05] )
dropped_score_tooltips.append( section['mark']['detail'] )
tickIndex += 1 + sectionSpacer
sectionIndex += 1
detail_tooltips['Dropped Scores'] = dropped_score_tooltips
tickIndex += 1
if section.get('prominent', False):
tickIndex += sectionSpacer
## ----------------------------- Grade overviewew bar ------------------------- ##
totalWeight = 0.0
sectionIndex = 0
totalScore = 0.0
tickIndex += sectionSpacer
series = categories.values()
overviewBarX = tickIndex
for section in grade_summary:
weighted_score = section['totalscore'] * section['weight']
summary_text = "{0} - {1:.1%} of a possible {2:.0%}".format(section['category'], weighted_score, section['weight'])
weighted_category_label = section['category'] + " - Weighted"
if section['totalscore'] > 0:
extraColorIndex = len(categories) #Keeping track of the next color to use for categories not in categories[]
for section in grade_summary['grade_breakdown']:
if section['percent'] > 0:
if section['category'] in categories:
color = categories[ section['category'] ]['color']
else:
color = colors[ extraColorIndex % len(colors) ]
extraColorIndex += 1
series.append({
'label' : weighted_category_label,
'data' : [ [overviewBarX, weighted_score] ],
'color' : colors[sectionIndex]
'label' : section['category'] + "-grade_breakdown",
'data' : [ [overviewBarX, section['percent']] ],
'color' : color
})
detail_tooltips[weighted_category_label] = [ summary_text ]
sectionIndex += 1
totalWeight += section['weight']
totalScore += section['totalscore'] * section['weight']
detail_tooltips[section['category'] + "-grade_breakdown"] = [ section['detail'] ]
ticks += [ [overviewBarX, "Total"] ]
tickIndex += 1 + sectionSpacer
totalScore = math.floor(grade_summary['percent'] * 100) / 100 #We floor it to the nearest percent, 80.9 won't show up like a 90 (an A)
detail_tooltips['Dropped Scores'] = dropped_score_tooltips
## ----------------------------- Grade cutoffs ------------------------- ##
grade_cutoff_ticks = [ [1, "100%"], [0, "0%"] ]
for grade in ['A', 'B', 'C']:
percent = grade_cutoffs[grade]
grade_cutoff_ticks.append( [ percent, "{0} {1:.0%}".format(grade, percent) ] )
%>
var series = ${ json.dumps(series) };
var series = ${ json.dumps( series ) };
var ticks = ${ json.dumps(ticks) };
var bottomTicks = ${ json.dumps(bottomTicks) };
var detail_tooltips = ${ json.dumps(detail_tooltips) };
var droppedScores = ${ json.dumps(droppedScores) };
var grade_cutoff_ticks = ${ json.dumps(grade_cutoff_ticks) }
//Alwasy be sure that one series has the xaxis set to 2, or the second xaxis labels won't show up
series.push( {label: 'Dropped Scores', data: droppedScores, points: {symbol: "cross", show: true, radius: 3}, bars: {show: false}, color: "#333"} );
......@@ -122,17 +117,17 @@ $(function () {
lines: {show: false, steps: false },
bars: {show: true, barWidth: 0.8, align: 'center', lineWidth: 0, fill: .8 },},
xaxis: {tickLength: 0, min: 0.0, max: ${tickIndex - sectionSpacer}, ticks: ticks, labelAngle: 90},
yaxis: {ticks: [[1, "100%"], [0.87, "A 87%"], [0.7, "B 70%"], [0.6, "C 60%"], [0, "0%"]], min: 0.0, max: 1.0, labelWidth: 50},
yaxis: {ticks: grade_cutoff_ticks, min: 0.0, max: 1.0, labelWidth: 50},
grid: { hoverable: true, clickable: true, borderWidth: 1,
markings: [ {yaxis: {from: 0.87, to: 1 }, color: "#ddd"}, {yaxis: {from: 0.7, to: 0.87 }, color: "#e9e9e9"},
{yaxis: {from: 0.6, to: 0.7 }, color: "#f3f3f3"}, ] },
markings: [ {yaxis: {from: ${grade_cutoffs['A']}, to: 1 }, color: "#ddd"}, {yaxis: {from: ${grade_cutoffs['B']}, to: ${grade_cutoffs['A']} }, color: "#e9e9e9"},
{yaxis: {from: ${grade_cutoffs['C']}, to: ${grade_cutoffs['B']} }, color: "#f3f3f3"}, ] },
legend: {show: false},
};
var $grade_detail_graph = $("#${graph_div_id}");
if ($grade_detail_graph.length > 0) {
var plot = $.plot($grade_detail_graph, series, options);
//We need to put back the plotting of the percent here
var o = plot.pointOffset({x: ${overviewBarX} , y: ${totalScore}});
$grade_detail_graph.append('<div style="position:absolute;left:' + (o.left - 12) + 'px;top:' + (o.top - 20) + 'px">${"{totalscore:.0%}".format(totalscore=totalScore)}</div>');
}
......
......@@ -153,6 +153,43 @@ div.profile-wrapper {
}
}
div#grade {
@include clearfix;
@extend .topbar;
padding-left: lh();
padding-right: lh();
p {
color: darken($cream, 80%);
float: left;
margin-bottom: 0;
font-size: 14px;
font-weight: bold;
text-transform: uppercase;
strong {
font-size: 18px;
position: relative;
top: 1px;
}
}
a {
background-color: darken($cream, 10%);
border: 1px solid darken($cream, 30%);
@include border-radius(3px);
@include box-shadow(inset 0 1px 0 lighten($cream, 5%));
display: block;
float: right;
font-weight: bold;
line-height: 1em;
margin-left: 10px;
margin-top: 9px;
padding: 8px;
text-shadow: 0 1px 0 lighten($cream, 5%);
}
}
div#grade-detail-graph {
width: 100%;
min-height: 300px;
......
......@@ -3,7 +3,7 @@
// Base layout
@import "base/reset", "base/font-face";
@import "base/variables", "base/functions", "base/extends", "base/base";
@import "layout/layout", "layout/header", "layout/footer", "layout/calculator", "layout/leanmodal";
@import "layout/layout", "layout/header", "layout/footer", "layout/calculator", "layout/leanmodal", "layout/survey";
@import "plugins/jquery-ui-1.8.16.custom", "plugins/jquery.qtip.min";
// pages
......
......@@ -205,6 +205,8 @@ div#pwd_reset {
}
}
div#survey,
div#cert_request,
div#apply_name_change,
div#change_email,
div#unenroll,
......@@ -249,3 +251,35 @@ div#feedback_div{
}
}
div#cert_request {
fieldset#cert_request_fieldset {
label {
font-weight: bold;
margin-bottom: 6px;
display: block;
}
li.verify{
@include clearfix;
input {
float: left;
}
label {
margin-left: 25px;
}
}
}
fieldset#survey_fieldset {
margin-top: lh();
}
div#cert_request_error {
display: block;
color: $mit-red;
font-weight: bold;
margin-bottom: 6px;
}
}
body.exit-survey, body.cert-req {
div.header-wrapper {
height: 47px;
header {
max-width: 600px;
nav {
display: none;
}
}
}
section.main-content {
max-width: 600px;
padding: lh();
h1 {
margin-top: 0;
}
fieldset#survey_fieldset {
> ul {
list-style: none;
> li {
border-top: 1px solid #eee;
padding: lh() 0;
margin-bottom: 0;
ul {
list-style: none;
li {
margin-bottom: 6px;
label {
display: block;
input {
margin-top: -1px;
margin-right: 6px;
}
}
}
}
textarea {
display: block;
width: 100%;
margin-top: lh(.5);
}
h2 {
margin-top: 0;
}
&.survey_short_field, &.survey_medium_field {
label {
font-weight: bold;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 6px;
}
}
}
}
}
}
}
body.cert-req {
fieldset#survey_fieldset {
border-top: 2px solid #ccc;
padding-top: lh();
}
}
<%page args="survey_list, **kwargs"/>
<%def name="survey_radio(question_name, label, choices)">
<li class="survey_radio">
<h2>${label}</h2>
<ul>
%for choice in choices:
<li>
<label>
<input type="radio" name="${question_name}" value="${choice}" align="BASELINE"/>${choice}
</label>
</li>
%endfor
</ul>
</li>
</%def>
<%def name="survey_select_many(question_name, label, choices)">
<li class="survey_select_many">
<h2>${label}</h2>
<ul>
%for choice in choices:
<li>
<label>
<input type="checkbox" name="${question_name}" value="${choice}" align="BASELINE"/>${choice}
</label>
</li>
%endfor
</ul>
</li>
</%def>
<%def name="survey_checkbox(question_name, label)">
<li class="survey_checkbox">
<label><input type="checkbox" name="${question_name}" value="true"/> ${label}</label>
</li>
</%def>
<%def name="survey_short_field(question_name, label)">
<li class="survey_short_field">
<label>${label}</label>
<input type="text" name="${question_name}"/>
</li>
</%def>
<%def name="survey_medium_field(question_name, label)">
<li class="survey_medium_field">
<label>${label}</label>
<textarea name="${question_name}"></textarea>
</li>
</%def>
<ul>
%for survey_question in survey_list:
%if survey_question['type'] == 'radio':
${survey_radio(survey_question['question_name'], survey_question['label'], survey_question['choices']) }
%elif survey_question['type'] == 'select_many':
${survey_select_many(survey_question['question_name'], survey_question['label'], survey_question['choices']) }
%elif survey_question['type'] == 'checkbox':
${survey_checkbox(survey_question['question_name'], survey_question['label']) }
%elif survey_question['type'] == 'short_field':
${survey_short_field(survey_question['question_name'], survey_question['label']) }
%elif survey_question['type'] == 'medium_field':
${survey_medium_field(survey_question['question_name'], survey_question['label']) }
%endif
%endfor
</ul>
......@@ -41,6 +41,12 @@ urlpatterns = ('',
url(r'^send_feedback$', 'util.views.send_feedback'),
)
if settings.END_COURSE_ENABLED:
urlpatterns += (
url(r'^certificate_request$', 'certificates.views.certificate_request'),
url(r'^exit_survey$','student.views.record_exit_survey'),
)
if settings.PERFSTATS:
urlpatterns += (url(r'^reprofile$','perfstats.views.end_profile'),)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment