Пример #1
0
class Notification(models.Model):
    project = models.ForeignKey(Project,
                                related_name='%(class)s_notifications')
    objects = RelatedProjectQuerySet.as_manager()

    class Meta(object):
        abstract = True
Пример #2
0
class Domain(models.Model):
    """A custom domain name for a project."""

    project = models.ForeignKey(Project, related_name='domains')
    domain = models.CharField(_('Domain'),
                              unique=True,
                              max_length=255,
                              validators=[validate_domain_name])
    machine = models.BooleanField(default=False,
                                  help_text=_('This Domain was auto-created'))
    cname = models.BooleanField(
        default=False, help_text=_('This Domain is a CNAME for the project'))
    canonical = models.BooleanField(
        default=False,
        help_text=_(
            'This Domain is the primary one where the documentation is '
            'served from'))
    https = models.BooleanField(
        _('Use HTTPS'),
        default=False,
        help_text=_('Always use HTTPS for this domain'))
    count = models.IntegerField(
        default=0,
        help_text=_('Number of times this domain has been hit'),
    )

    objects = RelatedProjectQuerySet.as_manager()

    class Meta(object):
        ordering = ('-canonical', '-machine', 'domain')

    def __str__(self):
        return '{domain} pointed at {project}'.format(
            domain=self.domain, project=self.project.name)

    def save(self, *args, **kwargs):  # pylint: disable=arguments-differ
        from readthedocs.projects import tasks
        parsed = urlparse(self.domain)
        if parsed.scheme or parsed.netloc:
            self.domain = parsed.netloc
        else:
            self.domain = parsed.path
        super(Domain, self).save(*args, **kwargs)
        broadcast(
            type='app',
            task=tasks.symlink_domain,
            args=[self.project.pk, self.pk],
        )

    def delete(self, *args, **kwargs):  # pylint: disable=arguments-differ
        from readthedocs.projects import tasks
        broadcast(
            type='app',
            task=tasks.symlink_domain,
            args=[self.project.pk, self.pk, True],
        )
        super(Domain, self).delete(*args, **kwargs)
Пример #3
0
class EnvironmentVariable(TimeStampedModel, models.Model):
    name = models.CharField(
        max_length=128,
        help_text=_('Name of the environment variable'),
    )
    value = models.CharField(
        max_length=2048,
        help_text=_('Value of the environment variable'),
    )
    project = models.ForeignKey(
        Project,
        on_delete=models.CASCADE,
        help_text=_('Project where this variable will be used'),
    )

    objects = RelatedProjectQuerySet.as_manager()

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):  # pylint: disable=arguments-differ
        self.value = quote(self.value)
        return super().save(*args, **kwargs)
Пример #4
0
class SearchQuery(TimeStampedModel):
    """Information about the search queries."""

    project = models.ForeignKey(
        Project,
        related_name='search_queries',
        on_delete=models.CASCADE,
    )
    version = models.ForeignKey(
        Version,
        verbose_name=_('Version'),
        related_name='search_queries',
        on_delete=models.CASCADE,
    )
    query = models.CharField(
        _('Query'),
        max_length=4092,
    )
    total_results = models.IntegerField(
        _('Total results'),
        default=0,
    )
    objects = RelatedProjectQuerySet.as_manager()

    class Meta:
        verbose_name = 'Search query'
        verbose_name_plural = 'Search queries'

    def __str__(self):
        return f'[{self.project.slug}:{self.version.slug}]: {self.query}'

    @classmethod
    def generate_queries_count_of_one_month(cls, project_slug):
        """
        Returns the total queries performed each day of the last 30 days (including today).

        Structure of returned data is compatible to make graphs.
        Sample returned data::
            {
                'labels': ['01 Jul', '02 Jul', '03 Jul'],
                'int_data': [150, 200, 143]
            }
        This data shows that there were 150 searches were made on 01 July,
        200 searches on 02 July and 143 searches on 03 July.
        """
        today = timezone.now().date()
        last_30th_day = timezone.now().date() - timezone.timedelta(days=30)

        qs = cls.objects.filter(
            project__slug=project_slug,
            created__date__lte=today,
            created__date__gte=last_30th_day,
        ).order_by('-created')

        # dict containing the total number of queries
        # of each day for the past 30 days (if present in database).
        count_dict = dict(
            qs.annotate(created_date=TruncDate('created')).values(
                'created_date').order_by('created_date').annotate(
                    count=Count('id')).values_list('created_date', 'count'))

        count_data = [
            count_dict.get(date) or 0 for date in _last_30_days_iter()
        ]

        # format the date value to a more readable form
        # Eg. `16 Jul`
        last_30_days_str = [
            timezone.datetime.strftime(date, '%d %b')
            for date in _last_30_days_iter()
        ]

        final_data = {
            'labels': last_30_days_str,
            'int_data': count_data,
        }

        return final_data
Пример #5
0
class SphinxDomain(TimeStampedModel):
    """
    Information from a project about it's Sphinx domains.

    This captures data about API objects that exist in that codebase.
    """

    project = models.ForeignKey(
        Project,
        related_name='sphinx_domains',
        on_delete=models.CASCADE,
    )
    version = models.ForeignKey(
        Version,
        verbose_name=_('Version'),
        related_name='sphinx_domains',
        on_delete=models.CASCADE,
    )
    html_file = models.ForeignKey(
        HTMLFile,
        related_name='sphinx_domains',
        null=True,
        on_delete=models.CASCADE,
    )
    commit = models.CharField(_('Commit'), max_length=255, null=True)
    build = models.IntegerField(_('Build id'), null=True)

    domain = models.CharField(
        _('Domain'),
        max_length=255,
    )
    name = models.CharField(
        _('Name'),
        max_length=4092,
    )
    display_name = models.CharField(
        _('Display Name'),
        max_length=4092,
    )
    type = models.CharField(
        _('Type'),
        max_length=255,
    )
    type_display = models.CharField(
        _('Type Display'),
        max_length=4092,
        null=True,
    )
    doc_name = models.CharField(
        _('Doc Name'),
        max_length=4092,
    )
    doc_display = models.CharField(
        _('Doc Display'),
        max_length=4092,
        null=True,
    )
    anchor = models.CharField(
        _('Anchor'),
        max_length=4092,
    )
    objects = RelatedProjectQuerySet.as_manager()

    def __str__(self):
        ret = f'''
            SphinxDomain [{self.project.slug}:{self.version.slug}]
            [{self.domain}:{self.type}] {self.name} ->
            {self.doc_name}
        '''.strip()
        if self.anchor:
            ret += f'#{self.anchor}'
        return ret

    @property
    def role_name(self):
        return f'{self.domain}:{self.type}'

    @property
    def docs_url(self):
        path = self.doc_name
        if self.anchor:
            path += f'#{self.anchor}'
        full_url = resolve(
            project=self.project,
            version_slug=self.version.slug,
            filename=path,
        )
        return full_url
Пример #6
0
class SearchQuery(TimeStampedModel):
    """Information about the search queries."""

    project = models.ForeignKey(
        Project,
        related_name='search_queries',
        on_delete=models.CASCADE,
    )
    version = models.ForeignKey(
        Version,
        verbose_name=_('Version'),
        related_name='search_queries',
        on_delete=models.CASCADE,
    )
    query = models.CharField(
        _('Query'),
        max_length=4092,
    )
    objects = RelatedProjectQuerySet.as_manager()

    class Meta:
        verbose_name = 'Search query'
        verbose_name_plural = 'Search queries'

    def __str__(self):
        return f'[{self.project.slug}:{self.version.slug}]: {self.query}'

    @classmethod
    def generate_queries_count_of_one_month(cls, project_slug):
        """
        Returns the total queries performed each day of the last 30 days (including today).

        Structure of returned data is compatible to make graphs.
        Sample returned data::
            {
                'labels': ['01 Jul', '02 Jul', '03 Jul'],
                'int_data': [150, 200, 143]
            }
        This data shows that there were 150 searches were made on 01 July,
        200 searches on 02 July and 143 searches on 03 July.
        """
        today = timezone.now().date()
        last_30th_day = timezone.now().date() - timezone.timedelta(days=30)

        # this includes the current day also
        last_31_days_iter = [
            last_30th_day + timezone.timedelta(days=n) for n in range(31)
        ]

        qs = cls.objects.filter(
            project__slug=project_slug,
            created__date__lte=today,
            created__date__gte=last_30th_day,
        ).order_by('-created')

        # dict containing the total number of queries
        # of each day for the past 30 days (if present in database).
        count_dict = dict(
            qs.annotate(created_date=TruncDate('created')).values(
                'created_date').order_by('created_date').annotate(
                    count=Count('id')).values_list('created_date', 'count'))

        count_data = [count_dict.get(date) or 0 for date in last_31_days_iter]

        # format the date value to a more readable form
        # Eg. `16 Jul`
        last_31_days_str = [
            timezone.datetime.strftime(date, '%d %b')
            for date in last_31_days_iter
        ]

        final_data = {
            'labels': last_31_days_str,
            'int_data': count_data,
        }

        return final_data

    @classmethod
    def generate_distribution_of_top_queries(cls, project_slug, n):
        """
        Returns top `n` most searched queries with their count.

        Structure of returned data is compatible to make graphs.
        Sample returned data::
            {
                'labels': ['read the docs', 'documentation', 'sphinx'],
                'int_data': [150, 200, 143]
            }
        This data shows that `read the docs` was searched 150 times,
        `documentation` was searched 200 times and `sphinx` was searched 143 times.
        """
        qs = cls.objects.filter(project__slug=project_slug)

        # total searches ever made
        total_count = len(qs)

        # search queries with their count
        # Eg. [('read the docs', 150), ('documentation', 200), ('sphinx', 143')]
        count_of_each_query = (qs.values('query').annotate(
            count=Count('id')).order_by('-count').values_list(
                'query', 'count'))

        # total number of searches made for top `n` queries
        count_of_top_n = sum([value[1] for value in count_of_each_query][:n])

        # total number of remaining searches
        count_of_other = total_count - count_of_top_n

        final_data = {
            'labels': [value[0] for value in count_of_each_query][:n],
            'int_data': [value[1] for value in count_of_each_query][:n],
        }

        if count_of_other:
            final_data['labels'].append('Other queries')
            final_data['int_data'].append(count_of_other)

        return final_data