medor.www
clone your own copy | download snapshot

Snapshots | iceberg

No images in this repository’s iceberg at this time

Inside this repository

models.py
text/x-python

Download raw (10.5 KB)

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import re
from urlparse import urlparse
from os.path import basename
from xml.etree import ElementTree

import markdown
import html5lib

from django.db import models
from django.db.models import Sum
from django.utils import timezone
# from django.contrib.webdesign.lorem_ipsum import paragraphs

from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType

from ckeditor.fields import RichTextField
from filer.fields.image import FilerImageField
from filer.models.imagemodels import Image

from publish.utils import typogrify

SENTENCE_LAST_CHARACTER = re.compile('[.!?]')


class Rubric(models.Model):
    """
    Represents a type of article and the optional rubric title andsubtitle that
    goes along.
    """
    title = models.CharField('titre de la rubrique', max_length=1024, blank=True)
    subtitle = models.CharField('sous-titre de la rubrique', max_length=1024, blank=True)
    type = models.CharField("type d'article", max_length=1024, blank=True)

    class Meta:
        verbose_name = "Rubrique"

    def __unicode__(self):
        return self.title or self.type or "Sans titre"


class License(models.Model):
    """
    Represents the intellectual property License,
    as attributed to an article or other creative work.
    """
    name = models.CharField("Nom", max_length=1024, blank=True, help_text="par exemple Creative Commons Attribution-ShareAlike 4.0 International")
    short_name = models.CharField("Nom abbrégé", max_length=20, blank=True, help_text="par exemple CC BY-SA 4.0")
    url = models.URLField("URL", blank=True, help_text="l'adresse à laquelle le texte de la licence est consultable")

    class Meta:
        verbose_name = "Licence"

    def __unicode__(self):
        return self.short_name or self.name or "Sans titre"


class Contributor(models.Model):
    """
    Represents a contributor, like a journalist or an illustrator.
    """
    name = models.CharField('nom', max_length=1024, blank=True)
    biography = RichTextField(blank=True)
    image = FilerImageField(blank=True, null=True)
    is_team = models.BooleanField(default="False")

    def __unicode__(self):
        return self.name

    @models.permalink
    def get_absolute_url(self):
        return ('contributor-detail', (), {'pk': self.pk})


class Role(models.Model):
    """
    Represents the role of a contributor, like "journalist" or an "illustrator".
    """
    name = models.CharField('nom', max_length=1024, blank=True)

    def __unicode__(self):
        return self.name


class Contribution(models.Model):
    """
    Represents a contribution
    """
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')
    contributor = models.ForeignKey(Contributor)
    role = models.ForeignKey(Role)
    license = models.ForeignKey(License, null=True, blank=True, verbose_name="licence")

    def __unicode__(self):
        return self.contributor.__unicode__()


def body_default():
    """
    Generates plain text Lorem Ipsum,
    then converts it to HTML via Markdown.
    """
    md = markdown.Markdown(output_format="html5", extensions=['extra'])
    return "foo"
    # return md.convert(u"\n\n".join(paragraphs(30)))


class Tag(models.Model):
    """
    Represents an Tag
    """
    name = models.CharField('name', max_length=1024)

    def __unicode__(self):
        return self.name


class Label(models.Model):
    """
    Represents an Label
    """
    name = models.CharField('name', max_length=1024)

    def __unicode__(self):
        return self.name


class Article(models.Model):
    """
    Represents an Article
    """
    STATUS_CHOICES = (
        (0, u'proposition'),
        (1, u'demande d\'évalutation par les pairs'),
        (2, u'demande de relecture'),
        (3, u'prêt')
    )

    creation_date = models.DateTimeField('date de création', auto_now_add=True, editable=True)
    modified_date = models.DateTimeField('date de modification', auto_now=True, editable=True)
    license = models.ForeignKey(License, null=True, blank=True, verbose_name="licence")
    title = models.CharField('titre', max_length=1024, blank=True)
    subtitle = models.CharField('sous-titre', max_length=1024, blank=True)
    rubric = models.ForeignKey(Rubric, blank=True, null=True, verbose_name="rubrique")
    slug = models.SlugField(max_length=1024, blank=True)
    lead = RichTextField('chapo', blank=True)
    body = RichTextField('article', blank=True, default=body_default)
    authors = models.CharField("auteurs", max_length=1024, blank=True)
    peer_reviewers = models.CharField("parrains ou marraines", max_length=1024, blank=True)
    contributions = GenericRelation(Contribution)
    status = models.PositiveSmallIntegerField('statut', choices=STATUS_CHOICES, default=0)
    published_online = models.BooleanField('publié en ligne', default=False)
    override_description = models.TextField('exergue spécifique pour le web', blank=True)
    override_image = FilerImageField(verbose_name='spécifier image aperçu', blank=True, null=True,
             on_delete=models.SET_NULL)
    related_articles = models.ManyToManyField("self", blank=True)
    tags = models.ManyToManyField(Tag, blank=True)
    labels = models.ManyToManyField(Label, blank=True)


    def save(self, *args, **kwargs):
        self.lead = typogrify(self.lead)
        self.body = typogrify(self.body)
        super(Article, self).save(*args, **kwargs) # Call the "real" save() method.

    def get_excerpt(self):
        """
        Look in the body text to find the ‘chapeau’, the lead text,
        that can be used as a description.
        """
        dom = html5lib.parseFragment(self.lead, treebuilder="etree", namespaceHTMLElements=False)
        for el in dom:
            if el.tag == "p":
                head = el.text or ""
                # el.text does not return the entire text if you have <p>Text with <em>child</em> tags</p>
                # cf http://stackoverflow.com/a/380717
                return "".join([head] + [ElementTree.tostring(e) for e in el.getchildren()])
        return u""

    @property
    def description(self):
        """
        The text that should be used as a description in the meta-data.

        The automatically deduced description can be overridden.
        """
        return self.override_description or self.get_excerpt()

    def get_image(self):
        """
        Look in the body text for the first image

        Try to find the associated filer object so we can make thumbnails
        """
        dom = html5lib.parseFragment(self.body, treebuilder="etree", namespaceHTMLElements=False)
        images = dom.findall('.//img')
        if images:
            img = images[0].get('src')            # u'https://medor.coop/media/filer_public/cb/1b/cb1b0760-5931-4766-b062-6ea821ba33c6/gent-cropped.png'
            img_path = urlparse(img).path         # u'/media/filer_public/cb/1b/cb1b0760-5931-4766-b062-6ea821ba33c6/gent-cropped.png'
            img_filename = basename(img_path)     # u'gent-cropped.png'
            for image in Image.objects.filter(original_filename__iexact=img_filename):
                if image.url == img_path:
                    return image
        return None

    @property
    def image(self):
        """
        Image associated with post (FilerImageField).
        """
        if self.override_image:
            return self.override_image
        return self.get_image()

    def __unicode__(self):
        return self.title or "Sans titre"

    @models.permalink
    def get_absolute_url(self):
        return ('article-detail-site', (), {'slug': self.slug})


class Issue(models.Model):
    """
    Represents an Issue of the Magazine
    """
    creation_date = models.DateTimeField("Date de création", auto_now_add=True)
    title = models.CharField("Titre", max_length=1024, blank=True)
    slug = models.SlugField("Slug", max_length=1024, blank=True, help_text="le texte utilisé pour construire les URLs")
    publish_date = models.DateTimeField("Date de publication", blank=True, null=True, help_text="la date de sortie du numéro")
    published_online = models.BooleanField('publié en ligne', default=False)
    contributions = GenericRelation(Contribution)
    cover = FilerImageField(blank=True, null=True)

    class Meta:
        verbose_name = "Numéro"

    def __unicode__(self):
        return self.title or "Sans titre"

    @models.permalink
    def get_absolute_url(self):
        return ('issue-detail-site', (), {'slug': self.slug})


class ArticleMembership(models.Model):
    """
    Registers articles in issues membership
    """
    in_toc = models.BooleanField('montré dans le table de matière', default=True)
    article = models.ForeignKey(Article, null=True, blank=True)
    issue = models.ForeignKey(Issue)
    order = models.PositiveIntegerField(default=0, blank=False, null=False)
    page_number = models.PositiveIntegerField("nombre de pages", default=1, blank=False, null=False)
    slug = models.SlugField(max_length=1024, blank=True)


    class Meta:
        ordering = ("order",)

    def __unicode__(self):
        return self.article.title if self.article else u"- - -"

    @property
    def single_page(self):
        return self.page_number == 1

    @property
    def folio(self):
        """
        Computes the page number of the first page of the article
        """
        if self.order == 1:
            return 1
        qs = self.issue.articlemembership_set.filter(order__lt=self.order)
        return qs.aggregate(page_count=Sum('page_number'))['page_count'] + 1

    @property
    def first_page(self):
        """
        With a less cryptic name
        """
        return self.folio

    @property
    def last_page(self):
        return self.folio + self.page_number - 1

    @property
    def is_even(self):
        """
        If true, article starts on a left-hand page (even page number)
        """
        return self.folio % 2 == 0


class ArticleMembershipWeb(models.Model):
    """
    Registers articles as part of the web timeline
    """
    article = models.ForeignKey(Article)
    order = models.PositiveIntegerField("ordre", default=0, blank=False, null=False)
    visible = models.BooleanField("publié en ligne", default=True)
    web_publish_date = models.DateTimeField("date de publication sur le web", default=timezone.now)

    class Meta:
        ordering = ("order",)
        verbose_name = verbose_name_plural = "timeline"

    def __unicode__(self):
        return self.article.title