lemoncurry/entries/models.py

202 lines
5.3 KiB
Python

from django.contrib.auth import get_user_model
from django.contrib.sites.models import Site
from django.db import models
from django.urls import reverse
from itertools import groupby
from mf2util import interpret
from slugify import slugify
from textwrap import shorten
from urllib.parse import urljoin
from meta.models import ModelMeta
from model_utils.models import TimeStampedModel
from users.models import Profile
from . import kinds
from lemoncurry import requests, utils
ENTRY_KINDS = [(k.id, k.id) for k in kinds.all]
class CatManager(models.Manager):
def from_name(self, name):
cat, created = self.get_or_create(name=name, slug=slugify(name))
return cat
class Cat(models.Model):
objects = CatManager()
name = models.CharField(max_length=255, unique=True)
slug = models.CharField(max_length=255, unique=True)
def __str__(self):
return '#' + self.name
@property
def url(self):
return reverse('entries:cat', args=(self.slug,))
class Meta:
ordering = ('name',)
class EntryManager(models.Manager):
def get_queryset(self):
qs = super(EntryManager, self).get_queryset()
return qs.select_related('author').prefetch_related('cats', 'syndications')
class Entry(ModelMeta, TimeStampedModel):
objects = EntryManager()
kind = models.CharField(
max_length=30,
choices=ENTRY_KINDS,
db_index=True,
default=ENTRY_KINDS[0][0]
)
name = models.CharField(max_length=100, blank=True)
photo = models.ImageField(blank=True)
content = models.TextField()
cats = models.ManyToManyField(Cat, related_name='entries')
in_reply_to = models.CharField(max_length=255, blank=True)
like_of = models.CharField(max_length=255, blank=True)
repost_of = models.CharField(max_length=255, blank=True)
author = models.ForeignKey(
get_user_model(),
related_name='entries',
on_delete=models.CASCADE,
)
@property
def reply_context(self):
if not self.in_reply_to:
return None
return interpret(
requests.mf2(self.in_reply_to).to_dict(),
self.in_reply_to
)
@property
def published(self):
return self.created
@property
def updated(self):
return self.modified
_metadata = {
'description': 'excerpt',
'image': 'image_url',
'twitter_creator': 'twitter_creator',
'og_profile_id': 'og_profile_id',
}
@property
def title(self):
if self.name:
return self.name
return shorten(utils.to_plain(self.paragraphs[0]), width=100, placeholder='')
@property
def excerpt(self):
try:
return utils.to_plain(self.paragraphs[0 if self.name else 1])
except IndexError:
return ' '
@property
def paragraphs(self):
lines = self.content.splitlines()
return [
"\n".join(para) for k, para in groupby(lines, key=bool) if k
]
@property
def twitter_creator(self):
return self.author.twitter_username
@property
def og_profile_id(self):
return self.author.facebook_id
@property
def image_url(self):
return self.photo.url if self.photo else self.author.avatar_url
def __str__(self):
return '{0} {1}: {2}'.format(self.kind, self.id, self.title)
def get_absolute_url(self):
return self.url
@property
def url(self):
kind = kinds.from_id[self.kind]
args = [kind.plural, self.id]
if kind.slug:
args.append(self.slug)
return reverse('entries:entry', args=args)
@property
def amp_url(self):
kind = kinds.from_id[self.kind]
args = [kind.plural, self.id]
if kind.slug:
args.append(self.slug)
return reverse('entries:entry_amp', args=args)
@property
def slug(self):
return slugify(self.name)
@property
def json_ld(self):
base = 'https://' + Site.objects.get_current().domain
url = urljoin(base, self.url)
posting = {
'@context': 'http://schema.org',
'@type': 'BlogPosting',
'@id': url,
'url': url,
'mainEntityOfPage': url,
'author': {
'@type': 'Person',
'url': urljoin(base, self.author.url),
'name': self.author.name,
},
'headline': self.title,
'description': self.excerpt,
'datePublished': self.created.isoformat(),
'dateModified': self.modified.isoformat(),
}
if self.photo:
posting['image'] = (urljoin(base, self.photo.url), )
return posting
class Meta:
verbose_name_plural = 'entries'
ordering = ['-created']
class SyndicationManager(models.Manager):
def get_queryset(self):
qs = super(SyndicationManager, self).get_queryset()
return qs.select_related('profile__site')
class Syndication(models.Model):
objects = SyndicationManager()
entry = models.ForeignKey(
Entry,
related_name='syndications',
on_delete=models.CASCADE
)
profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
url = models.CharField(max_length=255)
class Meta:
ordering = ['profile']