From 5dae386ea2b4588ccc476ada06af8dcae66316af Mon Sep 17 00:00:00 2001 From: Charles Connell Date: Wed, 30 Apr 2014 13:47:01 -0400 Subject: [PATCH] Notes have categories, fixes #367 --- karmaworld/apps/courses/views.py | 1 + karmaworld/apps/document_upload/forms.py | 2 +- karmaworld/apps/document_upload/views.py | 1 - .../0018_auto__add_field_note_category.py | 157 ++++++++++++++++++ karmaworld/apps/notes/models.py | 15 +- karmaworld/assets/css/note_course_pages.css | 4 + karmaworld/assets/js/filepicker.js | 2 + karmaworld/assets/js/note-list.js | 25 ++- .../templates/courses/course_detail.html | 15 +- karmaworld/templates/notes/note_detail.html | 8 + .../templates/notes/note_list_entry.html | 15 +- karmaworld/templates/partial/filepicker.html | 14 +- 12 files changed, 233 insertions(+), 26 deletions(-) create mode 100644 karmaworld/apps/notes/migrations/0018_auto__add_field_note_category.py diff --git a/karmaworld/apps/courses/views.py b/karmaworld/apps/courses/views.py index 6ba4632..b6d6fc8 100644 --- a/karmaworld/apps/courses/views.py +++ b/karmaworld/apps/courses/views.py @@ -116,6 +116,7 @@ class CourseDetailView(DetailView): # For the Filepicker Partial template kwargs['file_upload_form'] = FileUploadForm() + kwargs['note_categories'] = Note.NOTE_CATEGORIES if self.request.user.is_authenticated(): try: diff --git a/karmaworld/apps/document_upload/forms.py b/karmaworld/apps/document_upload/forms.py index 1484367..15afdbb 100644 --- a/karmaworld/apps/document_upload/forms.py +++ b/karmaworld/apps/document_upload/forms.py @@ -9,4 +9,4 @@ from karmaworld.apps.document_upload.models import RawDocument class RawDocumentForm(ModelForm): class Meta: model = RawDocument - fields = ('name', 'tags', 'course', 'fp_file', 'mimetype') + fields = ('name', 'tags', 'course', 'fp_file', 'mimetype', 'category') diff --git a/karmaworld/apps/document_upload/views.py b/karmaworld/apps/document_upload/views.py index 98b21e4..bee94b8 100644 --- a/karmaworld/apps/document_upload/views.py +++ b/karmaworld/apps/document_upload/views.py @@ -22,7 +22,6 @@ def save_fp_upload(request): raw_document.ip = request.META['REMOTE_ADDR'] raw_document.uploaded_at = datetime.datetime.utcnow() - # note that .save() has the side-effect of kicking of a celery processing task if request.user.is_authenticated(): raw_document.save() else: diff --git a/karmaworld/apps/notes/migrations/0018_auto__add_field_note_category.py b/karmaworld/apps/notes/migrations/0018_auto__add_field_note_category.py new file mode 100644 index 0000000..4fdf748 --- /dev/null +++ b/karmaworld/apps/notes/migrations/0018_auto__add_field_note_category.py @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as 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 'Note.category' + db.add_column(u'notes_note', 'category', + self.gf('django.db.models.fields.CharField')(max_length=50, null=True, blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Note.category' + db.delete_column(u'notes_note', 'category') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'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': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'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'}), + u'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'}) + }, + u'courses.course': { + 'Meta': {'ordering': "['-file_count', 'school', 'name']", 'unique_together': "(('name', 'school'),)", 'object_name': 'Course'}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'department': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['courses.Department']", 'null': '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'}), + u'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'}), + 'professor': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['courses.Professor']", 'null': 'True', 'blank': 'True'}), + 'school': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['courses.School']", 'null': 'True', 'blank': 'True'}), + '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'}) + }, + u'courses.department': { + 'Meta': {'unique_together': "(('name', 'school'),)", 'object_name': 'Department'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'school': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['courses.School']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '150', 'null': 'True'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '511', 'null': 'True', 'blank': 'True'}) + }, + u'courses.professor': { + 'Meta': {'unique_together': "(('name', 'email'),)", 'object_name': 'Professor'}, + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + u'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'}), + 'hashtag': ('django.db.models.fields.CharField', [], {'max_length': '16', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + u'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', [], {'unique': 'True', 'null': 'True', 'blank': 'True'}) + }, + u'licenses.license': { + 'Meta': {'object_name': 'License'}, + 'html': ('django.db.models.fields.TextField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}) + }, + u'notes.note': { + 'Meta': {'ordering': "['-uploaded_at']", 'unique_together': "(('fp_file', 'upstream_link'),)", 'object_name': 'Note'}, + 'category': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'course': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['courses.Course']"}), + 'flags': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'fp_file': ('django_filepicker.models.FPFileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'gdrive_url': ('django.db.models.fields.URLField', [], {'max_length': '1024', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}), + 'is_hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'license': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['licenses.License']", 'null': 'True', 'blank': 'True'}), + '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'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255'}), + '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'}), + 'upstream_link': ('django.db.models.fields.URLField', [], {'max_length': '1024', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}) + }, + u'notes.notemarkdown': { + 'Meta': {'object_name': 'NoteMarkdown'}, + 'markdown': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'note': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['notes.Note']", 'unique': 'True', 'primary_key': 'True'}) + }, + u'notes.useruploadmapping': { + 'Meta': {'unique_together': "(('user', 'fp_file'),)", 'object_name': 'UserUploadMapping'}, + 'fp_file': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) + }, + u'taggit.tag': { + 'Meta': {'object_name': 'Tag'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'}) + }, + u'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'taggit_taggeditem_tagged_items'", 'to': u"orm['contenttypes.ContentType']"}), + u'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': "u'taggit_taggeditem_items'", 'to': u"orm['taggit.Tag']"}) + } + } + + complete_apps = ['notes'] \ No newline at end of file diff --git a/karmaworld/apps/notes/models.py b/karmaworld/apps/notes/models.py index d3933d9..3f8e9f0 100644 --- a/karmaworld/apps/notes/models.py +++ b/karmaworld/apps/notes/models.py @@ -64,7 +64,20 @@ class Document(models.Model): tags = TaggableManager(blank=True) name = models.CharField(max_length=255, blank=True, null=True) slug = models.SlugField(max_length=255, unique=True) - + + LECTURE_NOTES = 'LECTURE_NOTES' + STUDY_GUIDE = 'STUDY_GUIDE' + SYLLABUS = 'SYLLABUS' + ASSIGNMENT = 'ASSIGNMENT' + OTHER = 'OTHER' + NOTE_CATEGORIES = ( + (LECTURE_NOTES, 'Lecture Notes'), + (STUDY_GUIDE, 'Study Guide'), + (SYLLABUS, 'Syllabus'), + (ASSIGNMENT, 'Assignment'), + (OTHER, 'Other'), + ) + category = models.CharField(max_length=50, choices=NOTE_CATEGORIES, blank=True, null=True) # license if different from default license = models.ForeignKey(License, blank=True, null=True) diff --git a/karmaworld/assets/css/note_course_pages.css b/karmaworld/assets/css/note_course_pages.css index 0bd7a7f..eca5bbd 100644 --- a/karmaworld/assets/css/note_course_pages.css +++ b/karmaworld/assets/css/note_course_pages.css @@ -137,3 +137,7 @@ button.add-note-btn { font-size: 0.8em; } +#note-category { + margin-bottom: 10px; +} + diff --git a/karmaworld/assets/js/filepicker.js b/karmaworld/assets/js/filepicker.js index b588ffb..88b2553 100644 --- a/karmaworld/assets/js/filepicker.js +++ b/karmaworld/assets/js/filepicker.js @@ -50,6 +50,7 @@ $(function(){ name = $(el).find('.intext').val(); fp_file = $(el).find('.fpurl').val(); tags = $(el).find('.taggit-tags').val(); + category = $(el).find('.category').val(); course = $(el).find('.course_id').val(); csrf = $(el).find('.csrf').val(); email = $('#id_email').val(); @@ -59,6 +60,7 @@ $(function(){ 'name': name, 'fp_file': fp_file, 'tags': tags, + 'category': category, 'course': course, 'csrfmiddlewaretoken': csrf, 'mimetype': mimetype, diff --git a/karmaworld/assets/js/note-list.js b/karmaworld/assets/js/note-list.js index 9022f01..4e351e1 100644 --- a/karmaworld/assets/js/note-list.js +++ b/karmaworld/assets/js/note-list.js @@ -9,21 +9,6 @@ $(function() { 'iDisplayLength': 20, // Position the filter bar at the top 'sDom': dataTable_sDom, - // Specify options for each column - "aoColumnDefs": [ - { - // 2nd element: thanks - "aTargets": [ 1 ], - "bSortable": true, - "bVisible": true - }, - { - // 1st element: date - "aTargets": [ 0 ], - "bSortable": true, - "bVisible": true - } - ], // Initial sorting 'aaSorting': [[1,'desc']] }); @@ -37,5 +22,15 @@ $(function() { // sort by current value of sort chooser, since // the browser may change this from our default dataTable.fnSort([[$('select.note-sort').val(), 'desc']]); + + $('select.note-category').change(function() { + var category = $(this).val(); + if (category === 'ALL') { + dataTable.fnFilter(''); + } else { + dataTable.fnFilter(category); + } + }); + } }); diff --git a/karmaworld/templates/courses/course_detail.html b/karmaworld/templates/courses/course_detail.html index 72df39b..a235aef 100644 --- a/karmaworld/templates/courses/course_detail.html +++ b/karmaworld/templates/courses/course_detail.html @@ -168,7 +168,7 @@
-
+
Sort By
+ +
+
Category
+
+ +
+
@@ -191,6 +203,7 @@ Date Popularity + Category Note diff --git a/karmaworld/templates/notes/note_detail.html b/karmaworld/templates/notes/note_detail.html index 4b2d616..a08222f 100644 --- a/karmaworld/templates/notes/note_detail.html +++ b/karmaworld/templates/notes/note_detail.html @@ -157,6 +157,14 @@
+ {% if note.category %} +
+
+ {{ note.get_category_display }} +
+
+ {% endif %} +
Tags: diff --git a/karmaworld/templates/notes/note_list_entry.html b/karmaworld/templates/notes/note_list_entry.html index 2675858..ebdc9a0 100644 --- a/karmaworld/templates/notes/note_list_entry.html +++ b/karmaworld/templates/notes/note_list_entry.html @@ -1,6 +1,7 @@ {{ note.uploaded_at|date:"U" }} {{ note.thanks|stringformat:"010g" }} + {{ note.category }}
@@ -20,13 +21,17 @@ {% else %}
{{ note.text|slice:":500" }} …
{% endif %} +
+ {% if note.category %} + {{ note.get_category_display }} + {% endif %} +
{% if note.tags.count > 0 %} - Tags: - {% for tag in note.tags.all %} - {{ tag.name }}{% if not forloop.last %}, {% endif %} - {% endfor %} - + Tags: + {% for tag in note.tags.all %} + {{ tag.name }}{% if not forloop.last %}, {% endif %} + {% endfor %} {% endif %}
diff --git a/karmaworld/templates/partial/filepicker.html b/karmaworld/templates/partial/filepicker.html index 3183ac8..933e8ae 100644 --- a/karmaworld/templates/partial/filepicker.html +++ b/karmaworld/templates/partial/filepicker.html @@ -34,12 +34,22 @@
-
+
-
+
+ +
+ +
+
+
-- 2.25.1