--- /dev/null
+# -*- 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):
+ # Removing unique constraint on 'Course', fields ['school', 'name', 'instructor_name']
+ db.delete_unique('courses_course', ['school_id', 'name', 'instructor_name'])
+
+ # Adding unique constraint on 'Course', fields ['department', 'name']
+ db.create_unique('courses_course', ['department_id', 'name'])
+
+ # Adding unique constraint on 'Professor', fields ['name', 'email']
+ db.create_unique('courses_professor', ['name', 'email'])
+
+ # Adding unique constraint on 'School', fields ['usde_id']
+ db.create_unique('courses_school', ['usde_id'])
+
+ # Adding unique constraint on 'Department', fields ['school', 'name']
+ db.create_unique('courses_department', ['school_id', 'name'])
+
+
+ def backwards(self, orm):
+ # Removing unique constraint on 'Department', fields ['school', 'name']
+ db.delete_unique('courses_department', ['school_id', 'name'])
+
+ # Removing unique constraint on 'School', fields ['usde_id']
+ db.delete_unique('courses_school', ['usde_id'])
+
+ # Removing unique constraint on 'Professor', fields ['name', 'email']
+ db.delete_unique('courses_professor', ['name', 'email'])
+
+ # Removing unique constraint on 'Course', fields ['department', 'name']
+ db.delete_unique('courses_course', ['department_id', 'name'])
+
+ # Adding unique constraint on 'Course', fields ['school', 'name', 'instructor_name']
+ db.create_unique('courses_course', ['school_id', 'name', 'instructor_name'])
+
+
+ models = {
+ 'courses.course': {
+ 'Meta': {'ordering': "['-file_count', 'school', 'name']", 'unique_together': "(('name', 'department'),)", 'object_name': 'Course'},
+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'department': ('django.db.models.fields.related.ForeignKey', [], {'to': "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'}),
+ '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.department': {
+ 'Meta': {'unique_together': "(('name', 'school'),)", 'object_name': 'Department'},
+ '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': "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'})
+ },
+ 'courses.professor': {
+ 'Meta': {'unique_together': "(('name', 'email'),)", 'object_name': 'Professor'},
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'courses.professoraffiliation': {
+ 'Meta': {'unique_together': "(('professor', 'department'),)", 'object_name': 'ProfessorAffiliation'},
+ 'department': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['courses.Department']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'professor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['courses.Professor']"})
+ },
+ 'courses.professortaught': {
+ 'Meta': {'unique_together': "(('professor', 'course'),)", 'object_name': 'ProfessorTaught'},
+ 'course': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['courses.Course']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'professor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['courses.Professor']"})
+ },
+ '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'}),
+ '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'})
+ }
+ }
+
+ complete_apps = ['courses']
\ No newline at end of file
from karmaworld.settings.manual_unique_together import auto_add_check_unique_together
+class SchoolManager(models.Manager):
+ """ Handle restoring data. """
+ def get_by_natural_key(self, usde_id):
+ """
+ Return a School defined by USDE number.
+ """
+ return self.get(usde_id=usde_id)
+
+
class School(models.Model):
""" A grouping that contains many courses """
+ objects = SchoolManager()
+
name = models.CharField(max_length=255)
slug = models.SlugField(max_length=150, null=True)
location = models.CharField(max_length=255, blank=True, null=True)
# Facebook keeps a unique identifier for all schools
facebook_id = models.BigIntegerField(blank=True, null=True)
# United States Department of Education institution_id
- usde_id = models.BigIntegerField(blank=True, null=True)
+ usde_id = models.BigIntegerField(blank=True, null=True, unique=True)
file_count = models.IntegerField(default=0)
priority = models.BooleanField(default=0)
alias = models.CharField(max_length=255, null=True, blank=True)
""" Sort School by file_count descending, name abc=> """
ordering = ['-file_count','-priority', 'name']
+ def natural_key(self):
+ """
+ A School is uniquely defined by USDE number.
+
+ Name should be unique, but there are some dupes in the DB.
+ """
+ return (self.usde_id,)
+
def __unicode__(self):
- return self.name
+ return u'School {0}: {1}'.format(self.usde_id, self.name)
def save(self, *args, **kwargs):
""" Save school and generate a slug if one doesn't exist """
self.save()
+class DepartmentManager(models.Manager):
+ """ Handle restoring data. """
+ def get_by_natural_key(self, name, school):
+ """
+ Return a Department defined by its name and school.
+ """
+ return self.get(name=name, school=school)
+
+
class Department(models.Model):
""" Department within a School. """
+ objects = DepartmentManager()
+
name = models.CharField(max_length=255)
school = models.ForeignKey(School) # Should this be optional ever?
slug = models.SlugField(max_length=150, null=True)
url = models.URLField(max_length=511, blank=True, null=True)
+ class Meta:
+ """
+ The same department name might exist across schools, but only once
+ per school.
+ """
+ unique_together = ('name', 'school',)
+
def __unicode__(self):
- return self.name
+ return u'Department: {0} at {1}'.format(self.name, unicode(self.school))
+
+ def natural_key(self):
+ """
+ A Department is uniquely defined by its school and name.
+ """
+ return (self.name, self.school.natural_key())
+ # Requires School to be dumped first
+ natural_key.dependencies = ['courses.school']
def save(self, *args, **kwargs):
""" Save department and generate a slug if one doesn't exist """
super(Department, self).save(*args, **kwargs)
+class ProfessorManager(models.Manager):
+ """ Handle restoring data. """
+ def get_by_natural_key(self, name, email):
+ """
+ Return a Professor defined by name and email address.
+ """
+ return self.get(name=name,email=email)
+
+
class Professor(models.Model):
"""
Track professors for courses.
"""
+ objects = ProfessorManager()
+
name = models.CharField(max_length=255)
email = models.EmailField(blank=True, null=True)
+ class Meta:
+ """
+ email should be unique, but some professors have no email address
+ in the database. For those cases, the name must be appended for
+ uniqueness.
+ """
+ unique_together = ('name', 'email',)
+
def __unicode__(self):
- return u'Professor: {0}'.format(self.name)
+ return u'Professor: {0} ({1})'.format(self.name, self.email)
+
+ def natural_key(self):
+ """
+ A Professor is uniquely defined by his/her name and email.
+ """
+ return (self.name,self.email)
+
+
+class ProfessorAffiliationManager(models.Manager):
+ """ Handle restoring data. """
+ def get_by_natural_key(self, prof, dept):
+ """
+ Return a ProfessorAffiliation defined by prof and department.
+ """
+ return self.get(professor=prof,department=dept)
class ProfessorAffiliation(models.Model):
"""
Track professors for departments. (many-to-many)
"""
- professor = models.ForeignKey(Professor)
+ objects = ProfessorAffiliationManager()
+
+ professor = models.ForeignKey(Professor)
department = models.ForeignKey(Department)
def __unicode__(self):
- return u'Professor {0} working for {1}'.format(self.professor.name, self.department.name)
+ return u'Professor {0} working for {1}'.format(unicode(self.professor), unicode(self.department))
class Meta:
- # many-to-many across both fields,
- # but (prof, dept) as a tuple should only appear once.
+ """
+ Many-to-many across both professor and department.
+ However, (prof, dept) as a tuple should only appear once.
+ """
unique_together = ('professor', 'department',)
+ def natural_key(self):
+ """
+ A ProfessorAffiliation is uniquely defined by the prof and department
+ """
+ return (self.professor.natural_key(), self.department.natural_key())
+ # Requires dependencies to be dumped first
+ natural_key.dependencies = ['courses.professor','courses.department']
+
+
+class CourseManager(models.Manager):
+ """ Handle restoring data. """
+ def get_by_natural_key(self, name, dept):
+ """
+ Return a Course defined by name and department.
+ """
+ return self.get(name=name,department=dept)
+
class Course(models.Model):
""" First class object that contains many notes.Note objects """
+ objects = CourseManager()
+
# Core metadata
name = models.CharField(max_length=255)
slug = models.SlugField(max_length=150, null=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']
- unique_together = ('school', 'name', 'instructor_name')
+ unique_together = ('name', 'department')
verbose_name = 'course'
verbose_name_plural = 'courses'
def __unicode__(self):
- return u"{0}: {1}".format(self.name, self.school)
+ return u"Course {0} in {1}".format(self.name, unicode(self.department))
+
+ def natural_key(self):
+ """
+ A Course is uniquely defined by its name and the department it is in.
+ """
+ return (self.name, self.department.natural_key())
+ # Requires dependencies to be dumped first
+ natural_key.dependencies = ['courses.department']
def get_absolute_url(self):
""" return url based on school slug and self slug """
self.save()
+class ProfessorTaughtManager(models.Manager):
+ """ Handle restoring data. """
+ def get_by_natural_key(self, prof, course):
+ """
+ Return a ProfessorTaught defined by professor and course.
+ """
+ return self.get(professor=prof, course=course)
+
+
class ProfessorTaught(models.Model):
"""
Track professors teaching courses. (many-to-many)
"""
+ objects = ProfessorTaughtManager()
+
professor = models.ForeignKey(Professor)
- course = models.ForeignKey(Course)
+ course = models.ForeignKey(Course)
def __unicode__(self):
- return u'Professor {0} taught {1}'.format(self.professor.name, self.course.name)
+ return u'Professor {0} taught {1}'.format(unicode(self.professor), unicode(self.course))
class Meta:
# many-to-many across both fields,
# but (prof, course) as a tuple should only appear once.
unique_together = ('professor', 'course',)
+ def natural_key(self):
+ """
+ A ProfessorTaught is uniquely defined by the prof and course.
+ """
+ return (self.professor.natural_key(), self.course.natural_key())
+ # Requires dependencies to be dumped first
+ natural_key.dependencies = ['courses.professor','courses.course']
+
# Enforce unique constraints even when we're using a database like
# SQLite that doesn't understand them
auto_add_check_unique_together(Course)
+auto_add_check_unique_together(Department)
+auto_add_check_unique_together(Professor)
auto_add_check_unique_together(ProfessorAffiliation)
auto_add_check_unique_together(ProfessorTaught)
--- /dev/null
+# -*- 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 unique constraint on 'RawDocument', fields ['upstream_link']
+ db.create_unique('document_upload_rawdocument', ['upstream_link'])
+
+ # Adding unique constraint on 'RawDocument', fields ['fp_file', 'upstream_link']
+ db.create_unique('document_upload_rawdocument', ['fp_file', 'upstream_link'])
+
+
+ def backwards(self, orm):
+ # Removing unique constraint on 'RawDocument', fields ['fp_file', 'upstream_link']
+ db.delete_unique('document_upload_rawdocument', ['fp_file', 'upstream_link'])
+
+ # Removing unique constraint on 'RawDocument', fields ['upstream_link']
+ db.delete_unique('document_upload_rawdocument', ['upstream_link'])
+
+
+ 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': "(('name', 'department'),)", 'object_name': 'Course'},
+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'department': ('django.db.models.fields.related.ForeignKey', [], {'to': "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'}),
+ '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.department': {
+ 'Meta': {'unique_together': "(('name', 'school'),)", 'object_name': 'Department'},
+ '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': "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'})
+ },
+ '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'}),
+ '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'})
+ },
+ 'document_upload.rawdocument': {
+ 'Meta': {'ordering': "['-uploaded_at']", 'unique_together': "(('fp_file', 'upstream_link'),)", 'object_name': 'RawDocument'},
+ 'course': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['courses.Course']"}),
+ 'fp_file': ('django_filepicker.models.FPFileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ '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'}),
+ 'is_processed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'license': ('django.db.models.fields.related.ForeignKey', [], {'to': "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', [], {'max_length': '255', 'null': 'True'}),
+ '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': "orm['users.KarmaUser']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'})
+ },
+ 'licenses.license': {
+ 'Meta': {'object_name': 'License'},
+ 'html': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'})
+ },
+ '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 = ['document_upload']
\ No newline at end of file
from karmaworld.apps.notes.models import Document
from karmaworld.apps.notes.models import Note
from karmaworld.apps.document_upload import tasks
+from karmaworld.settings.manual_unique_together import auto_add_check_unique_together
+
+
+class RawDocumentManager(models.Manager):
+ """ Handle restoring data. """
+ def get_by_natural_key(self, fp_file, upstream_link):
+ """
+ Return a RawDocument defined by its Filepicker and upstream URLs.
+ """
+ return self.get(fp_file=fp_file,upstream_link=upstream_link)
class RawDocument(Document):
+ objects = RawDocumentManager()
+
is_processed = models.BooleanField(default=False)
class Meta:
""" Sort files most recent first """
ordering = ['-uploaded_at']
-
+ unique_together = ('fp_file', 'upstream_link')
def __unicode__(self):
- return u"{0} @ {1}".format(self.ip, self.uploaded_at)
+ return u"RawDocument at {0} (from {1})".format(self.fp_file, self.upstream_link)
+
+ def natural_key(self):
+ """
+ A RawDocument is uniquely defined by both the Filepicker link and the
+ upstream link. The Filepicker link should be unique by itself, but
+ it may be null in the database, so the upstream link component should
+ resolve those cases.
+ """
+ return (self.fp_file, self.upstream_link)
def convert_to_note(self):
""" polymorph this object into a note.models.Note object """
super(RawDocument, self).save(*args, **kwargs)
if not self.is_processed:
tasks.process_raw_document.delay(self)
+
+
+auto_add_check_unique_together(RawDocument)
--- /dev/null
+# -*- 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 unique constraint on 'License', fields ['name']
+ db.create_unique('licenses_license', ['name'])
+
+
+ def backwards(self, orm):
+ # Removing unique constraint on 'License', fields ['name']
+ db.delete_unique('licenses_license', ['name'])
+
+
+ models = {
+ 'licenses.license': {
+ 'Meta': {'object_name': 'License'},
+ 'html': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'})
+ }
+ }
+
+ complete_apps = ['licenses']
\ No newline at end of file
from django.db import models
+class LicenseManager(models.Manager):
+ """ Handle restoring data. """
+ def get_by_natural_key(self, name):
+ """
+ Return a License defined by its brief name.
+ """
+ return self.get(name=name)
+
+
class License(models.Model):
"""
Track licenses for notes which are different from the default license
assumed for the site.
"""
+ objects = LicenseManager()
- name = models.CharField(max_length=80)
+ name = models.CharField(max_length=80,unique=True)
html = models.TextField()
def __unicode__(self):
return u'License: {0}'.format(self.name)
+
+ def natural_key(self):
+ """
+ A License is uniquely defined by its brief name.
+ """
+ return (self.name,)
--- /dev/null
+# -*- 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 unique constraint on 'Note', fields ['upstream_link']
+ db.create_unique('notes_note', ['upstream_link'])
+
+ # Adding unique constraint on 'Note', fields ['gdrive_url']
+ db.create_unique('notes_note', ['gdrive_url'])
+
+ # Adding unique constraint on 'Note', fields ['fp_file', 'upstream_link']
+ db.create_unique('notes_note', ['fp_file', 'upstream_link'])
+
+
+ def backwards(self, orm):
+ # Removing unique constraint on 'Note', fields ['fp_file', 'upstream_link']
+ db.delete_unique('notes_note', ['fp_file', 'upstream_link'])
+
+ # Removing unique constraint on 'Note', fields ['gdrive_url']
+ db.delete_unique('notes_note', ['gdrive_url'])
+
+ # Removing unique constraint on 'Note', fields ['upstream_link']
+ db.delete_unique('notes_note', ['upstream_link'])
+
+
+ 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': "(('name', 'department'),)", 'object_name': 'Course'},
+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'department': ('django.db.models.fields.related.ForeignKey', [], {'to': "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'}),
+ '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.department': {
+ 'Meta': {'unique_together': "(('name', 'school'),)", 'object_name': 'Department'},
+ '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': "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'})
+ },
+ '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'}),
+ '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'})
+ },
+ 'licenses.license': {
+ 'Meta': {'object_name': 'License'},
+ 'html': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'})
+ },
+ 'notes.note': {
+ 'Meta': {'unique_together': "(('fp_file', 'upstream_link'),)", 'object_name': 'Note'},
+ 'course': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['courses.Course']"}),
+ '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'}),
+ 'gdrive_url': ('django.db.models.fields.URLField', [], {'max_length': '1024', 'unique': 'True', '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.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': "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'}),
+ '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'}),
+ 'upstream_link': ('django.db.models.fields.URLField', [], {'max_length': '1024', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.KarmaUser']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+ 'year': ('django.db.models.fields.IntegerField', [], {'default': '2014', '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
from karmaworld.apps.courses.models import Course
from karmaworld.apps.licenses.models import License
from karmaworld.apps.notes.search import SearchIndex
+from karmaworld.settings.manual_unique_together import auto_add_check_unique_together
+
fs = FileSystemStorage(location=settings.MEDIA_ROOT)
+
def _choose_upload_to(instance, filename):
# /school/course/year/month/day
return u"{school}/{course}/{year}/{month}/{day}".format(
month=instance.uploaded_at.month,
day=instance.uploaded_at.day)
+
class Document(models.Model):
- """ An Abstract Base Class representing a document
- intended to be subclassed
+ """
+ An Abstract Base Class representing a document intended to be subclassed.
"""
course = models.ForeignKey(Course)
tags = TaggableManager(blank=True)
license = models.ForeignKey(License, blank=True, null=True)
# provide an upstream file link
- upstream_link = models.URLField(max_length=1024, blank=True, null=True)
+ upstream_link = models.URLField(max_length=1024, blank=True, null=True, unique=True)
# metadata relevant to the Upload process
user = models.ForeignKey('users.KarmaUser', blank=True, null=True, on_delete=SET_NULL)
abstract = True
ordering = ['-uploaded_at']
- def __unicode__(self):
- return u"Document: {1} -- {2}".format(self.name, self.uploaded_at)
-
def _generate_unique_slug(self):
""" generate a unique slug based on name and uploaded_at """
_slug = defaultfilters.slugify(self.name)
self._generate_unique_slug()
super(Document, self).save(*args, **kwargs)
+
+class NoteManager(models.Manager):
+ """ Handle restoring data. """
+ def get_by_natural_key(self, fp_file, upstream_link):
+ """
+ Return a Note defined by its Filepicker and upstream URLs.
+ """
+ return self.get(fp_file=fp_file,upstream_link=upstream_link)
+
+
class Note(Document):
- """ A django model representing an uploaded file and associated metadata.
+ """
+ A django model representing an uploaded file and associated metadata.
"""
+ objects = NoteManager()
+
# FIXME: refactor file choices after FP.io integration
UNKNOWN_FILE = '???'
FILE_TYPE_CHOICES = (
blank=True, null=True)
# Cache the Google drive file link
- gdrive_url = models.URLField(max_length=1024, blank=True, null=True)
+ gdrive_url = models.URLField(max_length=1024, blank=True, null=True, unique=True)
# Upload files to MEDIA_ROOT/notes/YEAR/MONTH/DAY, 2012/10/30/filename
pdf_file = models.FileField(
tweeted = models.BooleanField(default=False)
thanks = models.PositiveIntegerField(default=0)
+ class Meta:
+ unique_together = ('fp_file', 'upstream_link')
+
def __unicode__(self):
- return u"Note: {0} {1} -- {2}".format(self.file_type, self.name, self.uploaded_at)
+ return u"Note at {0} (from {1})".format(self.fp_file, self.upstream_link)
+ def natural_key(self):
+ """
+ A Note is uniquely defined by both the Filepicker link and the upstream
+ link. The Filepicker link should be unique by itself, but it may be
+ null in the database, so the upstream link component should resolve
+ those cases.
+ """
+ # gdrive_url might also fit the bill?
+ return (self.fp_file, self.upstream_link)
def get_absolute_url(self):
""" Resolve note url, use 'note' route and slug if slug
super(Note, self).save(*args, **kwargs)
+auto_add_check_unique_together(Note)
+
+
def update_note_counts(note_instance):
try:
# test if the course still exists, or if this is a cascade delete.
from django.db import models
+
+class KarmaUserManager(models.Manager):
+ """ Handle restoring data. """
+ def get_by_natural_key(self, email):
+ """
+ Return a KarmaUser defined by his/her email address.
+ """
+ return self.get(email=email)
+
+
class KarmaUser(models.Model):
- email = models.EmailField(blank=False, null=False, unique=True)
+ objects = KarmaUserManager()
+
+ email = models.EmailField(blank=False, null=False, unique=True)
def __unicode__(self):
- return u'{e}'.format(e=self.email)
+ return u'KarmaUser: {0}'.format(self.email)
+ def natural_key(self):
+ """
+ A KarmaUser is uniquely defined by his/her email address.
+ """
+ return (self.email,)