-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Admin table for course creators. #345
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
72999e8
Initial model.
2970f24
Initial admin table view.
f1f6acb
Updates to admin table view.
293ee0b
Update comment.
b53899a
Merge branch 'master' into christina/course-creator-table
2cceda9
Disable test point when run under CMS.
5e8e335
Pylint/pep8 cleanup.
28115af
The course creator view adds granted users course creator status.
69a9987
Remove test case moved to test_admin.
73f4b1c
Overloading methods does not work with variable names changed.
23ffa2a
Show note field, update filterable.
0bad629
Update migrate and syncdb commands so they run on CMS as well, delete…
5a3a03c
Change user to foreign key (tracks deletions).
5cd90d5
Clarify documentation.
6a49980
Add tests for permissions.
99584aa
pep8 cleanup
01557b2
Move call into authz methods out of model class.
283e2b2
Don't store abbreviations for state.
ee2d874
Remove extra blank lines.
d7946ef
Note about course creator table and rake command change.
f73aadb
Simplify boolean check.
1fd0451
pylint cleanup
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| """ | ||
| django admin page for the course creators table | ||
| """ | ||
|
|
||
| from course_creators.models import CourseCreator, update_creator_state | ||
| from course_creators.views import update_course_creator_group | ||
|
|
||
| from django.contrib import admin | ||
| from django.dispatch import receiver | ||
|
|
||
|
|
||
| def get_email(obj): | ||
| """ Returns the email address for a user """ | ||
| return obj.user.email | ||
|
|
||
| get_email.short_description = 'email' | ||
|
|
||
|
|
||
| class CourseCreatorAdmin(admin.ModelAdmin): | ||
| """ | ||
| Admin for the course creator table. | ||
| """ | ||
|
|
||
| # Fields to display on the overview page. | ||
| list_display = ['user', get_email, 'state', 'state_changed', 'note'] | ||
| readonly_fields = ['user', 'state_changed'] | ||
| # Controls the order on the edit form (without this, read-only fields appear at the end). | ||
| fieldsets = ( | ||
| (None, { | ||
| 'fields': ['user', 'state', 'state_changed', 'note'] | ||
| }), | ||
| ) | ||
| # Fields that filtering support | ||
| list_filter = ['state', 'state_changed'] | ||
| # Fields that search supports. | ||
| search_fields = ['user__username', 'user__email', 'state', 'note'] | ||
| # Turn off the action bar (we have no bulk actions) | ||
| actions = None | ||
|
|
||
| def has_add_permission(self, request): | ||
| return False | ||
|
|
||
| def has_delete_permission(self, request, obj=None): | ||
| return False | ||
|
|
||
| def has_change_permission(self, request, obj=None): | ||
| return request.user.is_staff | ||
|
|
||
| def save_model(self, request, obj, form, change): | ||
| # Store who is making the request. | ||
| obj.admin = request.user | ||
| obj.save() | ||
|
|
||
|
|
||
| admin.site.register(CourseCreator, CourseCreatorAdmin) | ||
|
|
||
|
|
||
| @receiver(update_creator_state, sender=CourseCreator) | ||
| def update_creator_group_callback(sender, **kwargs): | ||
| """ | ||
| Callback for when the model's creator status has changed. | ||
| """ | ||
| update_course_creator_group(kwargs['caller'], kwargs['user'], kwargs['add']) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| # -*- 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 'CourseCreator' | ||
| db.create_table('course_creators_coursecreator', ( | ||
| ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), | ||
| ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], unique=True)), | ||
| ('state_changed', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), | ||
| ('state', self.gf('django.db.models.fields.CharField')(default='unrequested', max_length=24)), | ||
| ('note', self.gf('django.db.models.fields.CharField')(max_length=512, blank=True)), | ||
| )) | ||
| db.send_create_signal('course_creators', ['CourseCreator']) | ||
|
|
||
|
|
||
| def backwards(self, orm): | ||
| # Deleting model 'CourseCreator' | ||
| db.delete_table('course_creators_coursecreator') | ||
|
|
||
|
|
||
| models = { | ||
| 'auth.group': { | ||
| 'Meta': {'object_name': 'Group'}, | ||
| 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), | ||
| 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) | ||
| }, | ||
| 'auth.permission': { | ||
| 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, | ||
| 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
| 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), | ||
| 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) | ||
| }, | ||
| 'auth.user': { | ||
| 'Meta': {'object_name': 'User'}, | ||
| 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
| 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), | ||
| 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
| 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), | ||
| 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), | ||
| 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
| 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
| 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
| 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
| 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), | ||
| 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), | ||
| 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) | ||
| }, | ||
| 'contenttypes.contenttype': { | ||
| 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, | ||
| 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
| 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
| 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) | ||
| }, | ||
| 'course_creators.coursecreator': { | ||
| 'Meta': {'object_name': 'CourseCreator'}, | ||
| 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
| 'note': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), | ||
| 'state': ('django.db.models.fields.CharField', [], {'default': "'unrequested'", 'max_length': '24'}), | ||
| 'state_changed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), | ||
| 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'}) | ||
| } | ||
| } | ||
|
|
||
| complete_apps = ['course_creators'] |
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| """ | ||
| Table for storing information about whether or not Studio users have course creation privileges. | ||
| """ | ||
| from django.db import models | ||
| from django.db.models.signals import post_init, post_save | ||
| from django.dispatch import receiver, Signal | ||
| from django.contrib.auth.models import User | ||
|
|
||
| from django.utils import timezone | ||
| from django.utils.translation import ugettext as _ | ||
|
|
||
| # A signal that will be sent when users should be added or removed from the creator group | ||
| update_creator_state = Signal(providing_args=["caller", "user", "add"]) | ||
|
|
||
|
|
||
| class CourseCreator(models.Model): | ||
| """ | ||
| Creates the database table model. | ||
| """ | ||
| UNREQUESTED = 'unrequested' | ||
| PENDING = 'pending' | ||
| GRANTED = 'granted' | ||
| DENIED = 'denied' | ||
|
|
||
| # Second value is the "human-readable" version. | ||
| STATES = ( | ||
| (UNREQUESTED, _(u'unrequested')), | ||
| (PENDING, _(u'pending')), | ||
| (GRANTED, _(u'granted')), | ||
| (DENIED, _(u'denied')), | ||
| ) | ||
|
|
||
| user = models.ForeignKey(User, help_text=_("Studio user"), unique=True) | ||
| state_changed = models.DateTimeField('state last updated', auto_now_add=True, | ||
| help_text=_("The date when state was last updated")) | ||
| state = models.CharField(max_length=24, blank=False, choices=STATES, default=UNREQUESTED, | ||
| help_text=_("Current course creator state")) | ||
| note = models.CharField(max_length=512, blank=True, help_text=_("Optional notes about this user (for example, " | ||
| "why course creation access was denied)")) | ||
|
|
||
| def __unicode__(self): | ||
| return u'%str | %str [%str] | %str' % (self.user, self.state, self.state_changed, self.note) | ||
|
|
||
|
|
||
| @receiver(post_init, sender=CourseCreator) | ||
| def post_init_callback(sender, **kwargs): | ||
| """ | ||
| Extend to store previous state. | ||
| """ | ||
| instance = kwargs['instance'] | ||
| instance.orig_state = instance.state | ||
|
|
||
|
|
||
| @receiver(post_save, sender=CourseCreator) | ||
| def post_save_callback(sender, **kwargs): | ||
| """ | ||
| Extend to update state_changed time and modify the course creator group in authz.py. | ||
| """ | ||
| instance = kwargs['instance'] | ||
| # We only wish to modify the state_changed time if the state has been modified. We don't wish to | ||
| # modify it for changes to the notes field. | ||
| if instance.state != instance.orig_state: | ||
| update_creator_state.send( | ||
| sender=sender, | ||
| caller=instance.admin, | ||
| user=instance.user, | ||
| add=instance.state == CourseCreator.GRANTED | ||
| ) | ||
| instance.state_changed = timezone.now() | ||
| instance.orig_state = instance.state | ||
| instance.save() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need docstrings here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't generally need them on methods that start with test_. I'll check if pylint/pep8 is complaining.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, no complaints about this.