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 ...@@ -15,7 +15,7 @@ from autoslug import AutoSlugField
from ndh.models import Links, NamedModel, TimeStampedModel from ndh.models import Links, NamedModel, TimeStampedModel
from ndh.utils import enum_to_choices, query_sum 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') logger = logging.getLogger('rainboard.models')
...@@ -266,6 +266,17 @@ class Project(Links, NamedModel, TimeStampedModel): ...@@ -266,6 +266,17 @@ class Project(Links, NamedModel, TimeStampedModel):
def gitlabciyml(self): def gitlabciyml(self):
return get_template('rainboard/gitlab-ci.yml').render({'project': 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): class Repo(TimeStampedModel):
name = models.CharField(max_length=200) name = models.CharField(max_length=200)
...@@ -643,10 +654,41 @@ class Tag(models.Model): ...@@ -643,10 +654,41 @@ class Tag(models.Model):
ordering = ('name',) ordering = ('name',)
unique_together = ('name', 'project') unique_together = ('name', 'project')
# TODO: later
# class Dockerfile(NamedModel, TimeStampedModel): class Contributor(models.Model):
# project = models.ForeignKey(Project, on_delete=models.CASCADE) projects = models.ManyToManyField(Project)
# target = models.PositiveSmallIntegerField(choices=enum_to_choices(TARGETS))
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): def get_default_forge(project):
...@@ -728,3 +770,46 @@ def update_travis(namespace, data): ...@@ -728,3 +770,46 @@ def update_travis(namespace, data):
else: else:
repo.travis_id = data['id'] repo.travis_id = data['id']
repo.save() 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): ...@@ -93,3 +93,12 @@ class ImageTable(StrippedTable):
class Meta: class Meta:
model = models.Image model = models.Image
fields = ('robotpkg', 'target', 'image', 'created') 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 @@ ...@@ -33,10 +33,11 @@
</div> </div>
<ul class="nav nav-tabs"> <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 '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 '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 '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 '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> </ul>
{% if table %} {% if table %}
......
...@@ -15,4 +15,5 @@ urlpatterns = [ ...@@ -15,4 +15,5 @@ urlpatterns = [
path('project/<str:slug>/repos', views.ProjectReposView.as_view(), name='project-repos'), 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>/branches', views.ProjectBranchesView.as_view(), name='project-branches'),
path('project/<str:slug>/images', views.ProjectImagesView.as_view(), name='project-images'), 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') ...@@ -11,6 +11,7 @@ logger = logging.getLogger('rainboard.utils')
SOURCES = IntEnum('Sources', 'github gitlab redmine robotpkg travis') SOURCES = IntEnum('Sources', 'github gitlab redmine robotpkg travis')
TARGETS = IntEnum('Targets', '14.04 16.04 17.10 18.04 dubnium') TARGETS = IntEnum('Targets', '14.04 16.04 17.10 18.04 dubnium')
INVALID_MAILS = ('localhost', 'none')
def slugify_with_dots(value): def slugify_with_dots(value):
...@@ -58,3 +59,7 @@ def update_robotpkg(path): ...@@ -58,3 +59,7 @@ def update_robotpkg(path):
except git.exc.GitCommandError: except git.exc.GitCommandError:
logger.error('Network error, retrying…') logger.error('Network error, retrying…')
git.Repo(str(path / 'wip' / '.git')).remotes.origin.pull() 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): ...@@ -71,3 +71,10 @@ class ProjectImagesView(ProjectTableView):
def get_object_list(self): def get_object_list(self):
return models.Image.objects.filter(robotpkg__project=self.object) 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