fmartingr.com-legacy/fmartingrcom/apps/blog/models.py

196 lines
5.0 KiB
Python

# -*- coding: utf-8 -*-
from datetime import datetime
import logging
import mimetypes
import os
import re
import shutil
import subprocess
from django.db import models
from django.conf import settings
from django.core.files import File
from django.utils.timezone import utc
from django.core.urlresolvers import reverse
from django.dispatch.dispatcher import receiver
from django.db.models.signals import pre_delete
from fmartingrcom.utils import sha1_checksum
import mistune
logger = logging.getLogger()
#
# ENTRY
#
class Entry(models.Model):
title = models.CharField(max_length=128)
date = models.DateTimeField()
content = models.TextField()
markdown = models.TextField(blank=True)
slug = models.SlugField(max_length=128)
draft = models.BooleanField(default=True)
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
editable=False,
related_name='author'
)
tags = models.ManyToManyField('Tag', blank=True)
def __unicode__(self):
return self.title
def status(self):
status = 'Published'
if self.date > datetime.now(tz=utc):
status = 'Scheduled'
if self.draft:
status = 'Draft'
return status
def get_absolute_url(self):
kwargs = {
'year': self.date.year,
'month': self.date.strftime("%m"),
'day': self.date.strftime("%d"),
'slug': self.slug
}
url = reverse('blog:item', kwargs=kwargs)
return url
def save(self, *args, **kwargs):
if self.markdown:
content = mistune.markdown(self.markdown)
self.content = content
result = super(Entry, self).save(*args, **kwargs)
return result
class Meta:
app_label = 'blog'
ordering = ['-date']
verbose_name_plural = 'Entries'
#
# TAG
#
class Tag(models.Model):
name = models.CharField(max_length=128)
color = models.CharField(max_length=6, blank=True)
def __unicode__(self):
return self.name
class Meta:
app_label = 'blog'
ordering = ['name']
#
# ATTACHMENT
#
def attachment_upload_to(instance, filename):
return os.path.join('{}_.{}'.format(
instance.media_path, instance.extension
))
class Attachment(models.Model):
filename = models.CharField(max_length=256)
sha1 = models.CharField(max_length=40)
mimetype = models.CharField(max_length=256, blank=True, null=True)
file = models.FileField(upload_to=attachment_upload_to)
creation_date = models.DateTimeField(auto_now_add=True)
modification_date = models.DateTimeField(auto_now=True)
@property
def extension(self):
return self.file.url.split('.')[-1]
@property
def url(self):
return self.file.url
@property
def media_path(self):
p1 = self.sha1[0:2]
p2 = self.sha1[2:4]
return 'attachment/{}/{}/{}/'.format(p1, p2, self.sha1)
@staticmethod
def upload_file(handler, filename=None):
"""
Given a certain file handler returns an `Image` objects
handler can be:
+ django.core.files.File instance __or subclass of__ (UploadedFile)
+ string with an absolute path to an image
"""
FLAGS = 'rw+'
# File Path
if isinstance(handler, str):
f = File(open(handler, FLAGS))
elif issubclass(handler.__class__, File) or isinstance(handler, File):
# Re-read with correct flags
f = handler
f.mode = FLAGS
else:
# I give up!
f = handler
if not filename:
filename = f.name.split('/')[-1]
sha1 = sha1_checksum(f)
# Check if file exists
try:
obj = Attachment.objects.get(sha1=sha1)
except Attachment.DoesNotExist:
obj = Attachment()
obj.file = f
obj.sha1 = sha1
obj.filename = filename
obj.mimetype = Attachment.get_mimetype(f, obj.filename)
obj.save()
return obj
@staticmethod
def get_mimetype(handler, filename):
re_mime_validate = re.compile('\w+/\w+(; \w+=[^;]+)*')
# subprocess
p = subprocess.Popen(('file', '--brief', '--mime-type', '-'),
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
stdout, stderr = p.communicate(handler.read())
if re_mime_validate.match(stdout):
mimetype = stdout.split()[0]
else:
# by python
mime, encoding = mimetypes.guess_type(filename)
mimetype = mime if mime else None
# rewind file descriptor
handler.file.seek(0)
return mimetype
class Meta:
verbose_name = 'Attachment'
@receiver(pre_delete, sender=Attachment)
def clean_files_on_delete(sender, instance, **kwargs):
try:
shutil.rmtree('/'.join(instance.file.path.split('/')[:-1]))
except Exception as error:
logger.warning(error, exc_info=True)