From 30792f2b271142d6a3ea7dcc8f7ca59db9a07d51 Mon Sep 17 00:00:00 2001 From: Seth Woodworth Date: Tue, 27 Aug 2013 13:51:53 -0400 Subject: [PATCH] Refactor Note > Document, an Abstract Base Class Here I have refactored all of the shared content of the Note model to the Document model so I can base Note and RawDocument on it I've also cleanedup .save() validation and generation --- karmaworld/apps/notes/models.py | 135 +++++++++++++++++++------------- 1 file changed, 82 insertions(+), 53 deletions(-) diff --git a/karmaworld/apps/notes/models.py b/karmaworld/apps/notes/models.py index dc57494..689b2a3 100644 --- a/karmaworld/apps/notes/models.py +++ b/karmaworld/apps/notes/models.py @@ -12,6 +12,7 @@ from django.conf import settings from django.core.files.storage import FileSystemStorage from django.db import models from django.template import defaultfilters +import django_filepicker from lxml.html import fromstring, tostring from oauth2client.client import Credentials from taggit.managers import TaggableManager @@ -25,9 +26,59 @@ except: fs = FileSystemStorage(location=settings.MEDIA_ROOT) -class Note(models.Model): +class Document(models.Model): + """ An Abstract Base Class representing a document + intended to be subclassed + + """ + course = models.ForeignKey(Course) + tags = TaggableManager(blank=True) + name = models.CharField(max_length=255, blank=True, null=True) + slug = models.SlugField(max_length=255, null=True) + + # metadata relevant to the Upload process + ip = models.IPAddressField(blank=True, null=True, + help_text=u"IP address of the uploader") + uploaded_at = models.DateTimeField(null=True, default=datetime.datetime.utcnow) + + + # if True, NEVER show this file + # WARNING: This may throw an error on migration + is_hidden = models.BooleanField(default=False) + + fp_note = django_filepicker.models.FPFileField( + upload_to='queue/%Y/%m/%j/', + null=True, blank=True, + help_text=u"An uploaded file reference from Filepicker.io") + + class Meta: + 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) + klass = self.__class__ + collision = klass.objects.filter(slug=self.slug) + if collision: + _slug = u"{0}-{1}-{2}-{3}".format( + _slug, self.uploaded_at.month, + self.uploaded_at.day, self.uploaded_at.microsecond) + self.slug = _slug + + def save(self, *args, **kwargs): + if self.name and not self.slug: + self._generate_unique_slug() + super(Document, self).save(*args, **kwargs) + +class Note(Document): """ A django model representing an uploaded file and associated metadata. """ + # FIXME: refactor file choices after FP.io integration UNKNOWN_FILE = '???' FILE_TYPE_CHOICES = ( ('doc', 'MS Word compatible file (.doc, .docx, .rtf, .odf)'), @@ -37,76 +88,44 @@ class Note(models.Model): (UNKNOWN_FILE, 'Unknown file'), ) - course = models.ForeignKey(Course) - # Tagging system - tags = TaggableManager(blank=True) - - name = models.CharField(max_length=255, blank=True, null=True) - slug = models.SlugField(max_length=255, null=True) - year = models.IntegerField(blank=True, null=True, - default=datetime.datetime.utcnow().year) - desc = models.TextField(max_length=511, blank=True, null=True) - uploaded_at = models.DateTimeField(null=True, default=datetime.datetime.utcnow) - - file_type = models.CharField(max_length=15, - choices=FILE_TYPE_CHOICES, - default=UNKNOWN_FILE, + file_type = models.CharField(max_length=15, \ + choices=FILE_TYPE_CHOICES, \ + default=UNKNOWN_FILE, \ blank=True, null=True) # Upload files to MEDIA_ROOT/notes/YEAR/MONTH/DAY, 2012/10/30/filename # FIXME: because we are adding files by hand in tasks.py, upload_to is being ignored for media files - note_file = models.FileField( - storage=fs, - upload_to="notes/%Y/%m/%j/", + pdf_file = models.FileField( \ + storage=fs, \ + upload_to="notes/%Y/%m/%j/",\ blank=True, null=True) - pdf_file = models.FileField( - storage=fs, - upload_to="notes/%Y/%m/%j/", + # No longer keeping a local copy backed by django + note_file = models.FileField( \ + storage=fs, \ + upload_to="notes/%Y/%m/%j/",\ blank=True, null=True) - ## post gdrive conversion data + # Google Drive URLs embed_url = models.URLField(max_length=1024, blank=True, null=True) download_url = models.URLField(max_length=1024, blank=True, null=True) - # for word processor documents + + # Generated by Google Drive by saved locally html = models.TextField(blank=True, null=True) text = models.TextField(blank=True, null=True) - # if True, NEVER show this file - draft = models.BooleanField(default=False) + # not using, but keeping old data + year = models.IntegerField(blank=True, null=True,\ + default=datetime.datetime.utcnow().year) + desc = models.TextField(max_length=511, blank=True, null=True) - class Meta: - """ Sort files by most recent first """ - ordering = ['-uploaded_at'] + is_flagged = models.BooleanField(default=False) + is_moderated = models.BooleanField(default=False) def __unicode__(self): - return u"{0}: {1} -- {2}".format(self.file_type, self.name, self.uploaded_at) + return u"Note: {0} {1} -- {2}".format(self.file_type, self.name, self.uploaded_at) - def save(self, *args, **kwargs): - """ override built-in save to ensure contextual self.name """ - # TODO: If self.name isn't set, generate one based on uploaded_name - # if we fail to set the Note.name earlier than this, use the saved filename - - # only generate a slug if the name has been set, and slug hasn't - if not self.slug and self.name: - slug = defaultfilters.slugify(self.name) - cursor = Note.objects.filter(slug=slug) - # If there are no other notes with this slug, then the slug does not need an id - if cursor.count() == 0: - self.slug = slug - else: - super(Note, self).save(*args, **kwargs) # generate self.id - self.slug = defaultfilters.slugify("%s %s" % (self.name, self.id)) - super(Note, self).save(*args, **kwargs) - - # Check if Note.uploaded_at is after Course.updated_at - if self.uploaded_at and self.uploaded_at > self.course.updated_at: - self.course.updated_at = self.uploaded_at - # if it is, update Course.updated_at - self.course.save() - - super(Note, self).save(*args, **kwargs) def get_absolute_url(self): """ Resolve note url, use 'note' route and slug if slug @@ -143,6 +162,16 @@ class Note(models.Model): self.save() return True, len(a_tags) + def _update_parent_updated_at(self): + """ update the parent Course.updated_at model + with the latest uploaded_at """ + self.course.updated_at = self.uploaded_at + self.course.save() + + def save(self, *args, **kwargs): + if self.uploaded_at and self.uploaded_at > self.course.updated_at: + self._update_parent_updated_at() + super(Note, self).save(*args, **kwargs) class DriveAuth(models.Model): -- 2.25.1