From 11a47520a2390754562014cfd3d8c2560098d5e6 Mon Sep 17 00:00:00 2001 From: Bryan Date: Mon, 30 Dec 2013 22:09:07 -0500 Subject: [PATCH] creating moderator site for moderating flagged documents. --- .../0003_auto__add_field_course_flags.py | 54 ++++++++ karmaworld/apps/courses/models.py | 3 + karmaworld/apps/moderation/__init__.py | 0 karmaworld/apps/moderation/admin.py | 39 ++++++ karmaworld/apps/moderation/moderator.py | 4 + ...__del_field_note_is_moderated__add_fiel.py | 121 ++++++++++++++++++ karmaworld/apps/notes/models.py | 5 +- karmaworld/settings/common.py | 3 +- karmaworld/urls.py | 4 + 9 files changed, 230 insertions(+), 3 deletions(-) create mode 100644 karmaworld/apps/courses/migrations/0003_auto__add_field_course_flags.py create mode 100644 karmaworld/apps/moderation/__init__.py create mode 100644 karmaworld/apps/moderation/admin.py create mode 100644 karmaworld/apps/moderation/moderator.py create mode 100644 karmaworld/apps/notes/migrations/0007_auto__del_field_note_is_flagged__del_field_note_is_moderated__add_fiel.py diff --git a/karmaworld/apps/courses/migrations/0003_auto__add_field_course_flags.py b/karmaworld/apps/courses/migrations/0003_auto__add_field_course_flags.py new file mode 100644 index 0000000..c90c4ee --- /dev/null +++ b/karmaworld/apps/courses/migrations/0003_auto__add_field_course_flags.py @@ -0,0 +1,54 @@ +# -*- 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 'Course.flags' + db.add_column('courses_course', 'flags', + self.gf('django.db.models.fields.IntegerField')(default=0), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Course.flags' + db.delete_column('courses_course', 'flags') + + + models = { + 'courses.course': { + 'Meta': {'ordering': "['-file_count', 'school', 'name']", 'unique_together': "(('school', 'name', 'instructor_name'),)", 'object_name': 'Course'}, + 'academic_year': ('django.db.models.fields.IntegerField', [], {'default': '2013', 'null': 'True', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'desc': ('django.db.models.fields.TextField', [], {'max_length': '511', 'null': 'True', 'blank': 'True'}), + 'file_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'flags': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instructor_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'instructor_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'school': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['courses.School']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '150', 'null': 'True'}), + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '511', 'null': 'True', 'blank': 'True'}) + }, + 'courses.school': { + 'Meta': {'ordering': "['-file_count', '-priority', 'name']", 'object_name': 'School'}, + 'alias': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'facebook_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'file_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'priority': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '150', 'null': 'True'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '511', 'blank': 'True'}), + 'usde_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['courses'] \ No newline at end of file diff --git a/karmaworld/apps/courses/models.py b/karmaworld/apps/courses/models.py index a3d6bae..c2ae953 100644 --- a/karmaworld/apps/courses/models.py +++ b/karmaworld/apps/courses/models.py @@ -75,6 +75,9 @@ class Course(models.Model): created_at = models.DateTimeField(auto_now_add=True) + # Number of times this course has been flagged as abusive/spam. + flags = models.IntegerField(default=0,null=False) + class Meta: ordering = ['-file_count', 'school', 'name'] diff --git a/karmaworld/apps/moderation/__init__.py b/karmaworld/apps/moderation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/karmaworld/apps/moderation/admin.py b/karmaworld/apps/moderation/admin.py new file mode 100644 index 0000000..4580c0f --- /dev/null +++ b/karmaworld/apps/moderation/admin.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# -*- coding:utf8 -*- +# Copyright (C) 2012 FinalsClub Foundation +""" Administration page for moderators """ + +from karmaworld.apps.courses.models import Course +from karmaworld.apps.courses.admin import CourseAdmin +from karmaworld.apps.notes.models import Note +from karmaworld.apps.notes.admin import NoteAdmin +from karmaworld.apps.moderation import moderator + +# Create a simple action to reset flags to zero. +# https://docs.djangoproject.com/en/1.4/ref/contrib/admin/actions/ +def reset_flags(modeladmin, request, queryset): + queryset.update(flags=0) +reset_flags.short_description = "Reset flags to 0" + +# Structure views of Course objects +class CourseModerator(CourseAdmin): + date_heirarchy = 'updated_at' + # Identify fields to display on the Change page + list_display = ('name', 'flags', 'school', 'academic_year', 'created_at', 'updated_at', 'instructor_name') + # Sort by highest number of flags first, and then by date for ties. + ordering = ('-flags', '-updated_at') + # Enable resetting flags + actions = (reset_flags,) + +# Structure views of Note objects +class NoteModerator(NoteAdmin): + date_heirarchy = 'uploaded_at' + # Identify fields to display on the Change page + list_display = ('name', 'flags', 'course', 'uploaded_at', 'ip') + # Sort by highest number of flags first, and then by date for ties + ordering = ('-flags', '-uploaded_at') + # Enable resetting flags + actions = (reset_flags,) + +moderator.site.register(Course, CourseModerator) +moderator.site.register(Note, NoteModerator) diff --git a/karmaworld/apps/moderation/moderator.py b/karmaworld/apps/moderation/moderator.py new file mode 100644 index 0000000..ac0900f --- /dev/null +++ b/karmaworld/apps/moderation/moderator.py @@ -0,0 +1,4 @@ +from django.contrib.admin.sites import AdminSite + +# Create a second administration site for use by moderators +site = AdminSite('moderator') diff --git a/karmaworld/apps/notes/migrations/0007_auto__del_field_note_is_flagged__del_field_note_is_moderated__add_fiel.py b/karmaworld/apps/notes/migrations/0007_auto__del_field_note_is_flagged__del_field_note_is_moderated__add_fiel.py new file mode 100644 index 0000000..c92ee55 --- /dev/null +++ b/karmaworld/apps/notes/migrations/0007_auto__del_field_note_is_flagged__del_field_note_is_moderated__add_fiel.py @@ -0,0 +1,121 @@ +# -*- 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): + # Deleting field 'Note.is_flagged' + db.delete_column('notes_note', 'is_flagged') + + # Deleting field 'Note.is_moderated' + db.delete_column('notes_note', 'is_moderated') + + # Adding field 'Note.flags' + db.add_column('notes_note', 'flags', + self.gf('django.db.models.fields.IntegerField')(default=0), + keep_default=False) + + + def backwards(self, orm): + # Adding field 'Note.is_flagged' + db.add_column('notes_note', 'is_flagged', + self.gf('django.db.models.fields.BooleanField')(default=False), + keep_default=False) + + # Adding field 'Note.is_moderated' + db.add_column('notes_note', 'is_moderated', + self.gf('django.db.models.fields.BooleanField')(default=False), + keep_default=False) + + # Deleting field 'Note.flags' + db.delete_column('notes_note', 'flags') + + + models = { + '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'}) + }, + 'courses.course': { + 'Meta': {'ordering': "['-file_count', 'school', 'name']", 'unique_together': "(('school', 'name', 'instructor_name'),)", 'object_name': 'Course'}, + 'academic_year': ('django.db.models.fields.IntegerField', [], {'default': '2013', 'null': 'True', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'desc': ('django.db.models.fields.TextField', [], {'max_length': '511', 'null': 'True', 'blank': 'True'}), + 'file_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'flags': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instructor_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'instructor_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'school': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['courses.School']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '150', 'null': 'True'}), + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '511', 'null': 'True', 'blank': 'True'}) + }, + 'courses.school': { + 'Meta': {'ordering': "['-file_count', '-priority', 'name']", 'object_name': 'School'}, + 'alias': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'facebook_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'file_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'priority': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '150', 'null': 'True'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '511', 'blank': 'True'}), + 'usde_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}) + }, + 'notes.note': { + 'Meta': {'ordering': "['-uploaded_at']", 'object_name': 'Note'}, + 'course': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['courses.Course']"}), + 'desc': ('django.db.models.fields.TextField', [], {'max_length': '511', 'null': 'True', 'blank': 'True'}), + 'download_url': ('django.db.models.fields.URLField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}), + 'embed_url': ('django.db.models.fields.URLField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}), + 'file_type': ('django.db.models.fields.CharField', [], {'default': "'???'", 'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'flags': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'fp_file': ('django_filepicker.models.FPFileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'note_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'pdf_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'thanks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'tweeted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.KarmaUser']", 'null': 'True', 'on_delete': 'models.SET_NULL'}), + 'year': ('django.db.models.fields.IntegerField', [], {'default': '2013', 'null': 'True', 'blank': 'True'}) + }, + 'taggit.tag': { + 'Meta': {'ordering': "['namespace', 'name']", 'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), + 'namespace': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'}) + }, + 'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) + }, + 'users.karmauser': { + 'Meta': {'object_name': 'KarmaUser'}, + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + } + } + + complete_apps = ['notes'] \ No newline at end of file diff --git a/karmaworld/apps/notes/models.py b/karmaworld/apps/notes/models.py index ed39e78..052bdb7 100644 --- a/karmaworld/apps/notes/models.py +++ b/karmaworld/apps/notes/models.py @@ -166,9 +166,10 @@ class Note(Document): default=datetime.datetime.utcnow().year) desc = models.TextField(max_length=511, blank=True, null=True) - is_flagged = models.BooleanField(default=False) - is_moderated = models.BooleanField(default=False) + # Number of times this note has been flagged as abusive/spam. + flags = models.IntegerField(default=0,null=False) + # Social media tracking tweeted = models.BooleanField(default=False) thanks = models.PositiveIntegerField(default=0) diff --git a/karmaworld/settings/common.py b/karmaworld/settings/common.py index 692e3be..0140618 100644 --- a/karmaworld/settings/common.py +++ b/karmaworld/settings/common.py @@ -213,7 +213,8 @@ LOCAL_APPS = ( 'karmaworld.apps.notes', 'karmaworld.apps.courses', 'karmaworld.apps.document_upload', - 'karmaworld.apps.users' + 'karmaworld.apps.users', + 'karmaworld.apps.moderation', ) # See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps diff --git a/karmaworld/urls.py b/karmaworld/urls.py index c247bcc..fd82721 100644 --- a/karmaworld/urls.py +++ b/karmaworld/urls.py @@ -18,6 +18,7 @@ from karmaworld.apps.courses.views import school_course_instructor_list from karmaworld.apps.notes.views import NoteView, thank_note from karmaworld.apps.notes.views import RawNoteDetailView from karmaworld.apps.notes.views import PDFView +from karmaworld.apps.moderation import moderator from karmaworld.apps.document_upload.views import save_fp_upload # See: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#hooking-adminsite-instances-into-your-urlconf @@ -50,6 +51,9 @@ urlpatterns = patterns('', url(r'^admin/', include(admin.site.urls)), # Grappelli django-admin improvment suite url(r'^grappelli/', include('grappelli.urls')), + # Moderator panel and documentation: + url(r'^moderator/doc/', include('django.contrib.admindocs.urls')), + url(r'^moderator/', include(moderator.site.urls)), ## Single-serving page URLpatterns url(r'^terms/$', direct_to_template, { 'template': 'terms.html' }, name='terms'), -- 2.25.1