Ejemplo n.º 1
0
class PublishedContent(models.Model):
    """A class that contains information on the published version of a content.

    Used for quick url resolution, quick listing, and to know where the public version of the files are.

    Linked to a ``PublishableContent`` for the rest. Don't forget to add a ``.prefetch_related("content")`` !!
    """

    # TODO: by playing with this class, it may solve most of the SEO problems !!

    class Meta:
        verbose_name = 'Contenu publié'
        verbose_name_plural = 'Contenus publiés'

    content = models.ForeignKey(PublishableContent, verbose_name='Contenu')

    content_type = models.CharField(max_length=10, choices=TYPE_CHOICES, db_index=True, verbose_name='Type de contenu')
    content_public_slug = models.CharField('Slug du contenu publié', max_length=80)
    content_pk = models.IntegerField('Pk du contenu publié', db_index=True)

    publication_date = models.DateTimeField('Date de publication', db_index=True, blank=True, null=True)
    sha_public = models.CharField('Sha1 de la version publiée', blank=True, null=True, max_length=80, db_index=True)

    must_redirect = models.BooleanField(
        'Redirection vers  une version plus récente', blank=True, db_index=True, default=False)

    objects = PublishedContentManager()

    def __unicode__(self):
        return _('Version publique de "{}"').format(self.content.title)

    def get_prod_path(self, relative=False):
        if not relative:
            return os.path.join(settings.ZDS_APP['content']['repo_public_path'], self.content_public_slug)
        else:
            return ''

    def get_absolute_url_online(self):
        """

        :return: the URL of the published content
        :rtype: str
        """
        reversed_ = ''

        if self.is_article():
            reversed_ = 'article'
        elif self.is_tutorial():
            reversed_ = 'tutorial'

        return reverse(reversed_ + ':view', kwargs={'pk': self.content_pk, 'slug': self.content_public_slug})

    def load_public_version_or_404(self):
        """
        :return: the public content
        :rtype: zds.tutorialv2.models.models_database.PublicContent
        :raise Http404: if the version is not available
        """
        return self.content.load_version_or_404(sha=self.sha_public, public=self)

    def load_public_version(self):
        """
        :rtype: zds.tutorialv2.models.models_database.PublicContent
        :return: the public content
        """
        return self.content.load_version(sha=self.sha_public, public=self)

    def is_article(self):
        """

        :return: ``True`` if it is an article, ``False`` otherwise.
        :rtype: bool
        """
        return self.content_type == "ARTICLE"

    def is_tutorial(self):
        """

        :return: ``True`` if it is an article, ``False`` otherwise.
        :rtype: bool
        """
        return self.content_type == "TUTORIAL"

    def get_extra_contents_directory(self):
        """
        :return: path to all the "extra contents"
        :rtype: str
        """
        return os.path.join(self.get_prod_path(), settings.ZDS_APP['content']['extra_contents_dirname'])

    def have_type(self, type_):
        """check if a given extra content exists

        :return: ``True`` if the file exists, ``False`` otherwhise
        :rtype: bool
        """

        allowed_types = ['pdf', 'md', 'html', 'epub', 'zip']

        if type_ in allowed_types:
            return os.path.isfile(
                os.path.join(self.get_extra_contents_directory(), self.content_public_slug + '.' + type_))

        return False

    def have_md(self):
        """Check if the markdown version of the content is available

        :return: ``True`` if available, ``False`` otherwise
        :rtype: bool
        """
        return self.have_type('md')

    def have_html(self):
        """Check if the html version of the content is available

        :return: ``True`` if available, ``False`` otherwise
        :rtype: bool
        """
        return self.have_type('html')

    def have_pdf(self):
        """Check if the pdf version of the content is available

        :return: ``True`` if available, ``False`` otherwise
        :rtype: bool
        """
        return self.have_type('pdf')

    def have_epub(self):
        """Check if the standard epub version of the content is available

        :return: ``True`` if available, ``False`` otherwise
        :rtype: bool
        """
        return self.have_type('epub')

    def have_zip(self):
        """Check if the standard epub version of the content is available

        :return: ``True`` if available, ``False`` otherwise
        :rtype: bool
        """
        return self.have_type('zip')

    def get_absolute_url_to_extra_content(self, type_):
        """Get the url that point to the extra content the user may want to download

        :param type_: the type inside allowed_type
        :type type_: str
        :return: URL to a given extra content (note that no check for existence is done)
        :rtype: str
        """

        allowed_types = ['pdf', 'md', 'html', 'epub', 'zip']

        if type_ in allowed_types:
            reversed_ = ''

            if self.is_article():
                reversed_ = 'article'
            elif self.is_tutorial():
                reversed_ = 'tutorial'

            return reverse(
                reversed_ + ':download-' + type_, kwargs={'pk': self.content_pk, 'slug': self.content_public_slug})

        return ''

    def get_absolute_url_md(self):
        """wrapper around ``self.get_absolute_url_to_extra_content('md')``

        :return: URL to the full markdown version of the published content
        :rtype: str
        """

        return self.get_absolute_url_to_extra_content('md')

    def get_absolute_url_html(self):
        """wrapper around ``self.get_absolute_url_to_extra_content('html')``

        :return: URL to the HTML version of the published content
        :rtype: str
        """

        return self.get_absolute_url_to_extra_content('html')

    def get_absolute_url_pdf(self):
        """wrapper around ``self.get_absolute_url_to_extra_content('pdf')``

        :return: URL to the PDF version of the published content
        :rtype: str
        """

        return self.get_absolute_url_to_extra_content('pdf')

    def get_absolute_url_epub(self):
        """wrapper around ``self.get_absolute_url_to_extra_content('epub')``

        :return: URL to the epub version of the published content
        :rtype: str
        """

        return self.get_absolute_url_to_extra_content('epub')

    def get_absolute_url_zip(self):
        """wrapper around ``self.get_absolute_url_to_extra_content('zip')``

        :return: URL to the zip archive of the published content
        :rtype: str
        """

        return self.get_absolute_url_to_extra_content('zip')
Ejemplo n.º 2
0
class PublishedContent(models.Model):
    """A class that contains information on the published version of a content.

    Used for quick url resolution, quick listing, and to know where the public version of the files are.

    Linked to a ``PublishableContent`` for the rest. Don't forget to add a ``.prefetch_related('content')`` !!
    """

    class Meta:
        verbose_name = 'Contenu publié'
        verbose_name_plural = 'Contenus publiés'

    content = models.ForeignKey(PublishableContent, verbose_name='Contenu')

    content_type = models.CharField(max_length=10, choices=TYPE_CHOICES, db_index=True, verbose_name='Type de contenu')
    content_public_slug = models.CharField('Slug du contenu publié', max_length=80)
    content_pk = models.IntegerField('Pk du contenu publié', db_index=True)

    publication_date = models.DateTimeField('Date de publication', db_index=True, blank=True, null=True)
    update_date = models.DateTimeField('Date de mise à jour', db_index=True, blank=True, null=True, default=None)
    sha_public = models.CharField('Sha1 de la version publiée', blank=True, null=True, max_length=80, db_index=True)
    nb_letter = models.IntegerField(default=None, null=True, verbose_name=b'Nombre de lettres du contenu', blank=True)

    must_redirect = models.BooleanField(
        'Redirection vers  une version plus récente', blank=True, db_index=True, default=False)

    authors = models.ManyToManyField(User, verbose_name='Auteurs', db_index=True)

    objects = PublishedContentManager()
    versioned_model = None

    # sizes contain a python dict (as a string in database) with all information about file sizes
    sizes = models.CharField('Tailles des fichiers téléchargeables', max_length=512, default='{}')

    def __str__(self):
        return _('Version publique de "{}"').format(self.content.title)

    def title(self):
        if self.versioned_model:
            return self.versioned_model.title
        else:
            return self.load_public_version().title

    def description(self):
        if self.versioned_model:
            return self.versioned_model.description
        else:
            return self.load_public_version().description

    def get_prod_path(self, relative=False):
        if not relative:
            return os.path.join(settings.ZDS_APP['content']['repo_public_path'], self.content_public_slug)
        else:
            return ''

    def get_absolute_url_online(self):
        """

        :return: the URL of the published content
        :rtype: str
        """
        reversed_ = self.content_type.lower()

        return reverse(reversed_ + ':view', kwargs={'pk': self.content_pk, 'slug': self.content_public_slug})

    def load_public_version_or_404(self):
        """
        :return: the public content
        :rtype: zds.tutorialv2.models.models_database.PublicContent
        :raise Http404: if the version is not available
        """
        self.versioned_model = self.content.load_version_or_404(sha=self.sha_public, public=self)
        return self.versioned_model

    def load_public_version(self):
        """
        :rtype: zds.tutorialv2.models.models_database.PublicContent
        :return: the public content
        """
        self.versioned_model = self.content.load_version(sha=self.sha_public, public=self)
        return self.versioned_model

    def is_article(self):
        """

        :return: ``True`` if it is an article, ``False`` otherwise.
        :rtype: bool
        """
        return self.content_type == 'ARTICLE'

    def is_tutorial(self):
        """

        :return: ``True`` if it is an article, ``False`` otherwise.
        :rtype: bool
        """
        return self.content_type == 'TUTORIAL'

    def get_extra_contents_directory(self):
        """
        :return: path to all the 'extra contents'
        :rtype: str
        """
        return os.path.join(self.get_prod_path(), settings.ZDS_APP['content']['extra_contents_dirname'])

    def have_type(self, type_):
        """check if a given extra content exists

        :return: ``True`` if the file exists, ``False`` otherwhise
        :rtype: bool
        """

        if type_ in ALLOWED_TYPES:
            return os.path.isfile(
                os.path.join(self.get_extra_contents_directory(), self.content_public_slug + '.' + type_))

        return False

    def have_md(self):
        """Check if the markdown version of the content is available

        :return: ``True`` if available, ``False`` otherwise
        :rtype: bool
        """
        return self.have_type('md')

    def have_html(self):
        """Check if the html version of the content is available

        :return: ``True`` if available, ``False`` otherwise
        :rtype: bool
        """
        return self.have_type('html')

    def have_pdf(self):
        """Check if the pdf version of the content is available

        :return: ``True`` if available, ``False`` otherwise
        :rtype: bool
        """
        return self.have_type('pdf')

    def have_epub(self):
        """Check if the standard epub version of the content is available

        :return: ``True`` if available, ``False`` otherwise
        :rtype: bool
        """
        return self.have_type('epub')

    def have_zip(self):
        """Check if the standard zip version of the content is available

        :return: ``True`` if available, ``False`` otherwise
        :rtype: bool
        """
        return self.have_type('zip')

    def get_size_file_type(self, type_):
        """
        Get the size of a given extra content.
        Is the size is not in database we get it and store it for next time.

        :return: size of file
        :rtype: int
        """
        if type_ in ALLOWED_TYPES:
            sizes = eval(str(self.sizes))
            try:
                size = sizes[type_]
            except KeyError:
                # if size is not in database we store it
                sizes[type_] = os.path.getsize(os.path.join(
                    self.get_extra_contents_directory(), self.content_public_slug + '.' + type_))
                self.sizes = sizes
                self.save()
                size = sizes[type_]
            return size
        return None

    def get_size_md(self):
        """Get the size of md

        :return: size of file
        :rtype: int
        """
        return self.get_size_file_type('md')

    def get_size_html(self):
        """Get the size of html

        :return: size of file
        :rtype: int
        """
        return self.get_size_file_type('html')

    def get_size_pdf(self):
        """Get the size of pdf

        :return: size of file
        :rtype: int
        """
        return self.get_size_file_type('pdf')

    def get_size_epub(self):
        """Get the size of epub

        :return: size of file
        :rtype: int
        """
        return self.get_size_file_type('epub')

    def get_size_zip(self):
        """Get the size of zip

        :return: size of file
        :rtype: int
        """
        return self.get_size_file_type('zip')

    def get_absolute_url_to_extra_content(self, type_):
        """Get the url that point to the extra content the user may want to download

        :param type_: the type inside allowed_type
        :type type_: str
        :return: URL to a given extra content (note that no check for existence is done)
        :rtype: str
        """

        if type_ in ALLOWED_TYPES:
            reversed_ = self.content_type.lower()

            return reverse(
                reversed_ + ':download-' + type_, kwargs={'pk': self.content_pk, 'slug': self.content_public_slug})

        return ''

    def get_absolute_url_md(self):
        """wrapper around ``self.get_absolute_url_to_extra_content('md')``

        :return: URL to the full markdown version of the published content
        :rtype: str
        """

        return self.get_absolute_url_to_extra_content('md')

    def get_absolute_url_html(self):
        """wrapper around ``self.get_absolute_url_to_extra_content('html')``

        :return: URL to the HTML version of the published content
        :rtype: str
        """

        return self.get_absolute_url_to_extra_content('html')

    def get_absolute_url_pdf(self):
        """wrapper around ``self.get_absolute_url_to_extra_content('pdf')``

        :return: URL to the PDF version of the published content
        :rtype: str
        """

        return self.get_absolute_url_to_extra_content('pdf')

    def get_absolute_url_epub(self):
        """wrapper around ``self.get_absolute_url_to_extra_content('epub')``

        :return: URL to the epub version of the published content
        :rtype: str
        """

        return self.get_absolute_url_to_extra_content('epub')

    def get_absolute_url_zip(self):
        """wrapper around ``self.get_absolute_url_to_extra_content('zip')``

        :return: URL to the zip archive of the published content
        :rtype: str
        """

        return self.get_absolute_url_to_extra_content('zip')

    def get_last_action_date(self):
        return self.update_date or self.publication_date

    def get_nb_letters(self, md_file_path=None):
        """ Compute the number of letters for a given content

        :param md_file_path: use another file to compute the number of letter rather than the default one.
        :type md_file_path: str
        :return: Number of letters in the md file
        :rtype: int
        """

        if not md_file_path:
            md_file_path = os.path.join(self.get_extra_contents_directory(), self.content_public_slug + '.md')

        try:
            with open(md_file_path, 'rb') as md_file:
                content = md_file.read().decode('utf-8')
            current_content = PublishedContent.objects.filter(content_pk=self.content_pk, must_redirect=False).first()
            if current_content:
                return len(content)
        except IOError as e:
            logger.warning('could not get file %s to compute nb letters (error=%s)', md_file_path, e)