readthedocs.org/readthedocs/oauth/models.py

201 lines
5.8 KiB
Python

# -*- coding: utf-8 -*-
"""OAuth service models."""
import json
from allauth.socialaccount.models import SocialAccount
from django.contrib.auth.models import User
from django.core.validators import URLValidator
from django.db import models
from django.db.models import Q
from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from readthedocs.projects.constants import REPO_CHOICES
from readthedocs.projects.models import Project
from .querysets import RemoteOrganizationQuerySet, RemoteRepositoryQuerySet
@python_2_unicode_compatible
class RemoteOrganization(models.Model):
"""
Organization from remote service.
This encapsulates both Github and Bitbucket
"""
# Auto fields
pub_date = models.DateTimeField(_('Publication date'), auto_now_add=True)
modified_date = models.DateTimeField(_('Modified date'), auto_now=True)
users = models.ManyToManyField(
User,
verbose_name=_('Users'),
related_name='oauth_organizations',
)
account = models.ForeignKey(
SocialAccount,
verbose_name=_('Connected account'),
related_name='remote_organizations',
null=True,
blank=True,
)
active = models.BooleanField(_('Active'), default=False)
slug = models.CharField(_('Slug'), max_length=255)
name = models.CharField(_('Name'), max_length=255, null=True, blank=True)
email = models.EmailField(_('Email'), max_length=255, null=True, blank=True)
avatar_url = models.URLField(_('Avatar image URL'), null=True, blank=True)
url = models.URLField(
_('URL to organization page'),
max_length=200,
null=True,
blank=True,
)
json = models.TextField(_('Serialized API response'))
objects = RemoteOrganizationQuerySet.as_manager()
def __str__(self):
return 'Remote organization: {name}'.format(name=self.slug)
def get_serialized(self, key=None, default=None):
try:
data = json.loads(self.json)
if key is not None:
return data.get(key, default)
return data
except ValueError:
pass
@python_2_unicode_compatible
class RemoteRepository(models.Model):
"""
Remote importable repositories.
This models Github and Bitbucket importable repositories
"""
# Auto fields
pub_date = models.DateTimeField(_('Publication date'), auto_now_add=True)
modified_date = models.DateTimeField(_('Modified date'), auto_now=True)
# This should now be a OneToOne
users = models.ManyToManyField(
User,
verbose_name=_('Users'),
related_name='oauth_repositories',
)
account = models.ForeignKey(
SocialAccount,
verbose_name=_('Connected account'),
related_name='remote_repositories',
null=True,
blank=True,
)
organization = models.ForeignKey(
RemoteOrganization,
verbose_name=_('Organization'),
related_name='repositories',
null=True,
blank=True,
)
active = models.BooleanField(_('Active'), default=False)
project = models.OneToOneField(
Project,
on_delete=models.SET_NULL,
related_name='remote_repository',
null=True,
blank=True,
)
name = models.CharField(_('Name'), max_length=255)
full_name = models.CharField(_('Full Name'), max_length=255)
description = models.TextField(
_('Description'),
blank=True,
null=True,
help_text=_('Description of the project'),
)
avatar_url = models.URLField(
_('Owner avatar image URL'),
null=True,
blank=True,
)
ssh_url = models.URLField(
_('SSH URL'),
max_length=512,
blank=True,
validators=[URLValidator(schemes=['ssh'])],
)
clone_url = models.URLField(
_('Repository clone URL'),
max_length=512,
blank=True,
validators=[
URLValidator(schemes=['http', 'https', 'ssh', 'git', 'svn']),
],
)
html_url = models.URLField(_('HTML URL'), null=True, blank=True)
private = models.BooleanField(_('Private repository'), default=False)
admin = models.BooleanField(_('Has admin privilege'), default=False)
vcs = models.CharField(
_('vcs'),
max_length=200,
blank=True,
choices=REPO_CHOICES,
)
json = models.TextField(_('Serialized API response'))
objects = RemoteRepositoryQuerySet.as_manager()
class Meta:
ordering = ['organization__name', 'name']
verbose_name_plural = 'remote repositories'
def __str__(self):
return 'Remote repository: {}'.format(self.html_url)
def get_serialized(self, key=None, default=None):
try:
data = json.loads(self.json)
if key is not None:
return data.get(key, default)
return data
except ValueError:
pass
@property
def clone_fuzzy_url(self):
"""Try to match against several permutations of project URL."""
def matches(self, user):
"""Projects that exist with repository URL already."""
# Support Git scheme GitHub url format that may exist in database
fuzzy_url = self.clone_url.replace('git://', '').replace('.git', '')
projects = (Project
.objects
.public(user)
.filter(Q(repo=self.clone_url) |
Q(repo__iendswith=fuzzy_url) |
Q(repo__iendswith=fuzzy_url + '.git'))) # yapf: disable
return [{
'id': project.slug,
'url': reverse(
'projects_detail',
kwargs={
'project_slug': project.slug,
},
),
} for project in projects]