Commit 4f0b8b86 authored by Tom Pillot's avatar Tom Pillot
Browse files

Add a view to show issues and pull requests

parent 2ec389b9
Pipeline #10511 failed with stage
in 1 minute and 19 seconds
......@@ -15,6 +15,17 @@ class ProjectFilter(django_filters.rest_framework.FilterSet):
fields = ('name', 'from_gepetto', 'archived')
class IssuePrFilter(django_filters.rest_framework.FilterSet):
namespace = django_filters.CharFilter(field_name='repo__namespace__name', label='namespace',
lookup_expr='icontains')
name = django_filters.CharFilter(field_name='repo__project__name', label='name', lookup_expr='icontains')
is_issue = django_filters.BooleanFilter(field_name='is_issue', label='issues')
class Meta:
model = models.IssuePr
fields = ('namespace', 'name', 'is_issue')
class ContributorFilter(django_filters.rest_framework.FilterSet):
name = django_filters.CharFilter(field_name='contributorname__name', label='name', lookup_expr='icontains')
mail = django_filters.CharFilter(field_name='contributormail__mail', label='mail', lookup_expr='icontains')
......
import re
from datetime import timedelta
import github
from django.conf import settings
from django.core.management import call_command
from django.core.management.base import BaseCommand
from django.db import IntegrityError
from django.db.models import F, Q
from django.utils import timezone
from github import Github
from rainboard.models import Branch, Forge, Image, Project, Repo, Robotpkg
from rainboard.models import Branch, Forge, Image, Project, Repo, Robotpkg, IssuePr
from rainboard.utils import SOURCES, update_robotpkg
......@@ -17,45 +21,77 @@ class Command(BaseCommand):
def log(message):
self.stdout.write(message)
log(f'updating forges')
for forge in Forge.objects.order_by('source'):
log(f' updating {forge}')
forge.get_projects()
# log(f'updating forges')
# for forge in Forge.objects.order_by('source'):
# log(f' updating {forge}')
# forge.get_projects()
#
# log(f'\nUpdating all repos\n')
# for repo in Repo.objects.filter(project__archived=False, project__from_gepetto=True):
# log(f' {repo}')
# repo.update()
#
# log(f'\nUpdating all branches\n')
# for branch in Branch.objects.filter(project__archived=False, project__from_gepetto=True):
# log(f' {branch.project} - {branch}')
# branch.update(pull=False)
#
# log(f'\nPulling Robotpkg\n')
# update_robotpkg(settings.RAINBOARD_RPKG)
#
# log(f'\nUpdating gepetto projects\n')
# for project in Project.objects.filter(archived=False, from_gepetto=True):
# log(f' {project}')
# project.update(only_main_branches=False)
#
# log(f'\nUpdating Robotpkg\n')
# for robotpkg in Robotpkg.objects.filter(project__archived=False, project__from_gepetto=True):
# log(f' {robotpkg}')
# robotpkg.update(pull=False)
#
# log(f'\nUpdating keep doc\n')
# Branch.objects.filter(Q(name__endswith='master') | Q(name__endswith='devel'),
# repo__namespace=F('project__main_namespace'),
# repo__forge__source=SOURCES.gitlab).update(keep_doc=True)
#
# log(f'\nDelete perso\n')
# call_command('delete_perso')
#
# log(f'\nclean obsolete Images\n')
# Image.objects.filter(robotpkg__project__archived=True).delete()
#
# log(f'\nLook for missing images\n')
# for img in Image.objects.filter(created__lt=timezone.now() - timedelta(days=7), target__active=True):
# log(f' {img}')
log(f'\nUpdating all repos\n')
for repo in Repo.objects.filter(project__archived=False, project__from_gepetto=True):
log(f' {repo}')
repo.update()
log('\nUpdating issues and pull requests\n')
for project in Project.objects.filter(archived=False, main_namespace__slug='gepetto'):
try:
gh = project.github()
main_repo = project.repo_set.filter(namespace=project.main_namespace, forge__source=SOURCES.github).first()
log(f'\nUpdating all branches\n')
for branch in Branch.objects.filter(project__archived=False, project__from_gepetto=True):
log(f' {branch.project} - {branch}')
branch.update(pull=False)
# Create new issues
for issue in gh.get_issues(state='open'):
url = re.sub('api\\.github\\.com/repos', 'github.com', issue.url)
obj, created = IssuePr.objects.get_or_create(title=issue.title,
repo=main_repo,
number=issue.number,
url=url,
is_issue=True)
log(f'\nPulling Robotpkg\n')
update_robotpkg(settings.RAINBOARD_RPKG)
# Create new pull requests
for pr in gh.get_pulls(state='open'):
url = re.sub('api\\.github\\.com/repos', 'github.com', pr.url)
url = re.sub('pulls/', 'pull/', url)
obj, created = IssuePr.objects.get_or_create(title=pr.title,
repo=main_repo,
number=pr.number,
url=url,
is_issue=False)
log(f'\nUpdating gepetto projects\n')
for project in Project.objects.filter(archived=False, from_gepetto=True):
log(f' {project}')
project.update(only_main_branches=False)
except github.UnknownObjectException as e:
log(f'\nProject not found: {project.main_namespace.slug_github}/{project.slug}\n')
log(f'\nUpdating Robotpkg\n')
for robotpkg in Robotpkg.objects.filter(project__archived=False, project__from_gepetto=True):
log(f' {robotpkg}')
robotpkg.update(pull=False)
log(f'\nUpdating keep doc\n')
Branch.objects.filter(Q(name__endswith='master') | Q(name__endswith='devel'),
repo__namespace=F('project__main_namespace'),
repo__forge__source=SOURCES.gitlab).update(keep_doc=True)
log(f'\nDelete perso\n')
call_command('delete_perso')
log(f'\nclean obsolete Images\n')
Image.objects.filter(robotpkg__project__archived=True).delete()
log(f'\nLook for missing images\n')
for img in Image.objects.filter(created__lt=timezone.now() - timedelta(days=7), target__active=True):
log(f' {img}')
# Update all issues and pull requests, delete closed ones
for issue_pr in IssuePr.objects.all():
issue_pr.update()
......@@ -13,26 +13,12 @@ def add_project(apps, schema_editor):
Namespace = apps.get_model('rainboard', 'Namespace')
Forge = apps.get_model('rainboard', 'Forge')
git_project = Project(
Project.objects.create(
name='example-adder',
public=True,
main_namespace=Namespace.objects.get(name='tpillot'),
main_forge=Forge.objects.get(name='Gitlab'),
license=None,
homepage=None,
description=None,
version=None,
updated=None,
tests=True,
docs=True,
from_gepetto=True,
cmake_name=None,
archived=False,
suffix='',
allow_format_failure=True,
has_python=True
from_gepetto=True
)
git_project.save()
class Migration(migrations.Migration):
......
# Generated by Django 3.0.8 on 2020-08-03 15:10
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('rainboard', '0047_example-adder'),
]
operations = [
migrations.CreateModel(
name='IssuePr',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200)),
('number', models.PositiveSmallIntegerField()),
('days_since_updated', models.PositiveSmallIntegerField(blank=True, null=True)),
('url', models.URLField()),
('is_issue', models.BooleanField(default=True)),
('repo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='rainboard.Repo')),
],
options={
'unique_together': {('repo', 'number', 'is_issue')},
},
),
]
from django.db import migrations
import itertools
from rainboard.models import Robotpkg, Project
from django.conf import settings
path = settings.RAINBOARD_RPKG
def add_robotpkg(apps, schema_editor):
project = Project.objects.get(slug='example-adder')
for slug in [project.slug, project.slug.replace('_', '-')]:
for pkg in itertools.chain(path.glob(f'*/{slug}{project.suffix}'), path.glob(f'*/py-{slug}{project.suffix}')):
obj, created = Robotpkg.objects.get_or_create(name=pkg.name, category=pkg.parent.name, project=project)
if created:
print(project, pkg)
obj.update(pull=False)
for rpkg in project.robotpkg_set.all():
rpkg.update_images()
class Migration(migrations.Migration):
dependencies = [
('rainboard', '0047_example-adder'),
]
operations = [
# migrations.RunPython(add_robotpkg),
]
import logging
import re
from datetime import datetime
from subprocess import check_output
import git
......@@ -664,6 +665,27 @@ class Repo(TimeStampedModel):
logger.error(str(self.delete()))
class IssuePr(models.Model):
title = models.CharField(max_length=200)
repo = models.ForeignKey(Repo, on_delete=models.CASCADE)
number = models.PositiveSmallIntegerField()
days_since_updated = models.PositiveSmallIntegerField(blank=True, null=True)
url = models.URLField(max_length=200)
is_issue = models.BooleanField(default=True)
class Meta:
unique_together = ('repo', 'number', 'is_issue')
def update(self):
gh = self.repo.project.github()
issue_pr = gh.get_issue(number=self.number) if self.is_issue else gh.get_pull(number=self.number)
self.days_since_updated = (datetime.now() - issue_pr.updated_at).days
if issue_pr.state == 'closed':
self.delete()
else:
self.save()
class Commit(NamedModel, TimeStampedModel):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
......@@ -958,7 +980,7 @@ class CIBuild(models.Model):
branch = models.ForeignKey(Branch, on_delete=models.CASCADE)
class Meta:
ordering = ('-started', )
ordering = ('-started',)
def url(self):
if self.repo.forge.source == SOURCES.github:
......@@ -975,7 +997,7 @@ class CIJob(models.Model):
branch = models.ForeignKey(Branch, on_delete=models.CASCADE)
class Meta:
ordering = ('-started', )
ordering = ('-started',)
class Tag(models.Model):
......@@ -984,7 +1006,7 @@ class Tag(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
class Meta:
ordering = ('name', )
ordering = ('name',)
unique_together = ('name', 'project')
def __str__(self):
......
......@@ -121,3 +121,19 @@ class ContributorProjectTable(ContributorTable):
class Meta:
fields = ('names', 'mails', 'projects')
class IssuePrTable(StrippedTable):
name = tables.Column(accessor='repo.project.name')
class Meta:
model = models.IssuePr
fields = ('repo.namespace', 'name', 'title', 'url', 'days_since_updated')
order_by = '-days_since_updated'
def render_name(self, record):
return record.repo.project.get_link()
def render_url(self, record):
rendered_name = 'issue #' if record.is_issue else 'PR #'
return mark_safe(f'<a href="{record.url}">{rendered_name}{record.number}</a>')
\ No newline at end of file
......@@ -11,6 +11,7 @@
{% navbar_item 'rainboard:graph' 'Graph' %}
{% navbar_item 'rainboard:contributors' 'Contributors' %}
{% navbar_item 'rainboard:gepetto' 'Gepetto' %}
{% navbar_item 'rainboard:issues_pr' 'Issues / Pull requests' %}
{% endblock %}
{% block scripts %}
......
{% extends 'base.html' %}
{% load django_tables2 bootstrap4 %}
{% block content %}
<h1>Issues and Pull Requests</h1>
{% if filter %}
<form action="" method="get" class="form form-inline">
{% bootstrap_form filter.form layout='inline' %}
{% bootstrap_button 'filter' %}
</form>
{% endif %}
{% render_table table %}
{% endblock %}
......@@ -34,6 +34,7 @@ urlpatterns = [
path('project/<str:slug>/images', views.ProjectImagesView.as_view(), name='project-images'),
path('project/<str:slug>/contributors', views.ProjectContributorsView.as_view(), name='project-contributors'),
path('project/<str:slug>/.gitlab-ci.yml', views.ProjectGitlabView.as_view(), name='project-gitlab'),
path('issues', views.IssuesPrView.as_view(), name='issues_pr'),
path('doc', views.json_doc, name='doc'),
path('images', views.images_list, name='images'),
path('docker', views.docker, name='docker'),
......
......@@ -100,6 +100,12 @@ class ContributorsView(SingleTableMixin, DistinctMixin, FilterView):
strict = False
class IssuesPrView(SingleTableMixin, FilterView):
model = models.IssuePr
table_class = tables.IssuePrTable
filterset_class = filters.IssuePrFilter
def json_doc(request):
"""
Get the list of project / namespace / branch of which we want to keep the doc
......@@ -146,7 +152,7 @@ def ordered_projects(request):
class AuthenticatedOrReadOnlyModelViewSet(viewsets.ModelViewSet):
permission_classes = (permissions.IsAuthenticatedOrReadOnly, )
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
class NamespaceViewSet(AuthenticatedOrReadOnlyModelViewSet):
......
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