Commit 03989f7e authored by Guilhem Saurel's avatar Guilhem Saurel
Browse files

Contributors

parent 8362a642
# Generated by Django 2.0.2 on 2018-02-15 18:19
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('rainboard', '0003_image'),
]
operations = [
migrations.CreateModel(
name='Contributor',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('projects', models.ManyToManyField(to='rainboard.Project')),
],
),
migrations.CreateModel(
name='ContributorMail',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('mail', models.EmailField(max_length=254, unique=True)),
('invalid', models.BooleanField(default=False)),
('contributor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='rainboard.Contributor')),
],
),
migrations.CreateModel(
name='ContributorName',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, unique=True)),
('contributor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='rainboard.Contributor')),
],
),
]
......@@ -15,7 +15,7 @@ from autoslug import AutoSlugField
from ndh.models import Links, NamedModel, TimeStampedModel
from ndh.utils import enum_to_choices, query_sum
from .utils import SOURCES, TARGETS, api_next, slugify_with_dots
from .utils import SOURCES, TARGETS, api_next, slugify_with_dots, invalid_mail
logger = logging.getLogger('rainboard.models')
......@@ -266,6 +266,17 @@ class Project(Links, NamedModel, TimeStampedModel):
def gitlabciyml(self):
return get_template('rainboard/gitlab-ci.yml').render({'project': self})
def contributors(self, update=False):
if self.main_branch() is None:
return []
if update:
for guy in self.git().git.shortlog('-nse').split('\n'):
name, mail = guy[7:-1].split(' <')
contributor = get_contributor(name, mail)
contributor.projects.add(self)
contributor.save()
return self.contributor_set.all()
class Repo(TimeStampedModel):
name = models.CharField(max_length=200)
......@@ -643,10 +654,41 @@ class Tag(models.Model):
ordering = ('name',)
unique_together = ('name', 'project')
# TODO: later
# class Dockerfile(NamedModel, TimeStampedModel):
# project = models.ForeignKey(Project, on_delete=models.CASCADE)
# target = models.PositiveSmallIntegerField(choices=enum_to_choices(TARGETS))
class Contributor(models.Model):
projects = models.ManyToManyField(Project)
def __str__(self):
name = self.contributorname_set.first()
mail = self.contributormail_set.first()
return f'{name} <{mail}>'
def names(self):
return ', '.join(str(name) for name in self.contributorname_set.all())
def mails(self):
return ', '.join(str(mail) for mail in self.contributormail_set.filter(invalid=False))
def contributed(self):
return ', '.join(str(project) for project in self.projects.all())
class ContributorName(models.Model):
contributor = models.ForeignKey(Contributor, on_delete=models.CASCADE, blank=True, null=True)
name = models.CharField(max_length=200, unique=True)
def __str__(self):
return self.name
class ContributorMail(models.Model):
contributor = models.ForeignKey(Contributor, on_delete=models.CASCADE, blank=True, null=True)
mail = models.EmailField(unique=True)
invalid = models.BooleanField(default=False)
def __str__(self):
return self.mail
def get_default_forge(project):
......@@ -728,3 +770,46 @@ def update_travis(namespace, data):
else:
repo.travis_id = data['id']
repo.save()
def get_contributor(name, mail):
cname, name_created = ContributorName.objects.get_or_create(name=name)
cmail, mail_created = ContributorMail.objects.get_or_create(mail=mail, defaults={'invalid': invalid_mail(mail)})
if name_created or mail_created:
if name_created and mail_created:
contributor = Contributor.objects.create()
cname.contributor = contributor
cmail.contributor = contributor
cname.save()
cmail.save()
if mail_created:
contributor = cname.contributor
cmail.contributor = contributor
cmail.save()
if name_created:
contributor = cmail.contributor
cname.contributor = cmail.contributor
cname.save()
elif cname.contributor == cmail.contributor or invalid_mail(mail):
contributor = cname.contributor
elif cname.contributor is None and cmail.contributor is not None:
contributor = cmail.contributor
cname.contributor = contributor
cname.save()
elif cmail.contributor is None:
contributor = cname.contributor
cmail.contributor = contributor
cmail.save()
else:
contributor, fake = cname.contributor, cmail.contributor
logger.warning(f'merging {contributor} & {fake}')
if fake.id < contributor.id:
contributor, fake = fake, contributor
for n in fake.contributorname_set.all():
n.contributor = contributor
n.save()
for m in fake.contributormail_set.all():
m.contributor = contributor
m.save()
fake.delete()
return contributor
......@@ -93,3 +93,12 @@ class ImageTable(StrippedTable):
class Meta:
model = models.Image
fields = ('robotpkg', 'target', 'image', 'created')
class ContributorTable(StrippedTable):
names = tables.Column(accessor='names', orderable=False)
mails = tables.Column(accessor='mails', orderable=False)
class Meta:
model = models.Contributor
fields = ('names', 'mails')
......@@ -33,10 +33,11 @@
</div>
<ul class="nav nav-tabs">
<li role="presentation" {% if 'robotpkg' in request.path %}class="active"{% endif %}><a href="{% url 'rainboard:project' slug=project.slug %}">Robotpkg</a></h2></li>
<li role="presentation" {% if 'repos' in request.path %}class="active"{% endif %}><a href="{% url 'rainboard:project-repos' slug=project.slug %}">Repos </a></h2></li>
<li role="presentation" {% if 'branches' in request.path %}class="active"{% endif %}><a href="{% url 'rainboard:project-branches' slug=project.slug %}">Branches</a></h2></li>
<li role="presentation" {% if 'images' in request.path %}class="active"{% endif %}><a href="{% url 'rainboard:project-images' slug=project.slug %}">Images </a></h2></li>
<li role="presentation" {% if 'robotpkg' in request.path %}class="active"{% endif %}><a href="{% url 'rainboard:project' slug=project.slug %}">Robotpkg </a></h2></li>
<li role="presentation" {% if 'repos' in request.path %}class="active"{% endif %}><a href="{% url 'rainboard:project-repos' slug=project.slug %}">Repos </a></h2></li>
<li role="presentation" {% if 'branches' in request.path %}class="active"{% endif %}><a href="{% url 'rainboard:project-branches' slug=project.slug %}">Branches </a></h2></li>
<li role="presentation" {% if 'images' in request.path %}class="active"{% endif %}><a href="{% url 'rainboard:project-images' slug=project.slug %}">Images </a></h2></li>
<li role="presentation" {% if 'contributors' in request.path %}class="active"{% endif %}><a href="{% url 'rainboard:project-contributors' slug=project.slug %}">Contributors</a></h2></li>
</ul>
{% if table %}
......
......@@ -15,4 +15,5 @@ urlpatterns = [
path('project/<str:slug>/repos', views.ProjectReposView.as_view(), name='project-repos'),
path('project/<str:slug>/branches', views.ProjectBranchesView.as_view(), name='project-branches'),
path('project/<str:slug>/images', views.ProjectImagesView.as_view(), name='project-images'),
path('project/<str:slug>/contributors', views.ProjectContributorsView.as_view(), name='project-contributors'),
]
......@@ -11,6 +11,7 @@ logger = logging.getLogger('rainboard.utils')
SOURCES = IntEnum('Sources', 'github gitlab redmine robotpkg travis')
TARGETS = IntEnum('Targets', '14.04 16.04 17.10 18.04 dubnium')
INVALID_MAILS = ('localhost', 'none')
def slugify_with_dots(value):
......@@ -58,3 +59,7 @@ def update_robotpkg(path):
except git.exc.GitCommandError:
logger.error('Network error, retrying…')
git.Repo(str(path / 'wip' / '.git')).remotes.origin.pull()
def invalid_mail(mail):
return any(invalid in mail for invalid in INVALID_MAILS)
......@@ -71,3 +71,10 @@ class ProjectImagesView(ProjectTableView):
def get_object_list(self):
return models.Image.objects.filter(robotpkg__project=self.object)
class ProjectContributorsView(ProjectTableView):
table_class = tables.ContributorTable
def get_object_list(self):
return self.object.contributors()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment