euscanwww: charts, about, etc...

Signed-off-by: Corentin Chary <corentincj@iksaif.net>
This commit is contained in:
Corentin Chary 2011-04-25 22:27:32 +02:00
parent 13dd433996
commit 0aba96f66f
30 changed files with 504 additions and 121 deletions

115
euscanwww/euscan/charts.py Normal file
View File

@ -0,0 +1,115 @@
import os.path
import time
from euscanwww import settings
from django.db.models import F, Sum, Max
from euscan.models import Version, Package, Herd, Maintainer
from euscan.models import CategoryLog
import pylab
import matplotlib
CHARTS_ROOT = os.path.join(settings.MEDIA_ROOT, "charts")
CHARTS_URL = os.path.join(settings.MEDIA_URL, "charts")
CHARTS_TTL = (24 * 60 * 60)
pylab.rcParams['font.size'] = 10.0
pylab.rcParams['axes.titlesize'] = 10.0
pylab.rcParams['xtick.labelsize'] = 8.0
pylab.rcParams['legend.fontsize'] = 8.0
def xint(i):
try:
return int(i)
except:
return 0
def chart_alive(name):
path = os.path.join(CHARTS_ROOT, name)
if not os.path.exists(path):
return False
if os.path.getmtime(__file__) > os.path.getmtime(path):
return False
if os.path.getmtime(path) + CHARTS_TTL < time.time():
return False
return True
def chart_name(name, **kwargs):
if 'category' in kwargs and kwargs['category']:
name += '-%s' % kwargs['category']
if 'herd' in kwargs and kwargs['herd']:
name += '-h-%d' % kwargs['herd'].id
if 'maintainer' in kwargs and kwargs['maintainer']:
name += '-m-%d' % kwargs['maintainer'].id
return name + ".png"
def packages(**kwargs):
packages = Package.objects
if 'category' in kwargs and kwargs['category']:
packages = packages.filter(category=kwargs['category'])
if 'herd' in kwargs and kwargs['herd']:
packages = packages.filter(herds__id=kwargs['herd'].id)
if 'maintainer' in kwargs and kwargs['maintainer']:
packages = packages.filter(maintainers__id=kwargs['maintainer'].id)
return packages
def cached_pylab_chart(f):
def new_f(*args, **kwds):
name = chart_name(f.func_name, **kwds)
if not chart_alive(name):
f(*args, **kwds)
pylab.savefig(os.path.join(CHARTS_ROOT, name))
pylab.close()
return name
new_f.func_name = f.func_name
return new_f
@cached_pylab_chart
def pie_versions(**kwargs):
n_packaged = xint(packages(**kwargs).aggregate(Sum('n_packaged'))['n_packaged__sum'])
n_overlay = xint(packages(**kwargs).aggregate(Sum('n_overlay'))['n_overlay__sum'])
n_versions = xint(packages(**kwargs).aggregate(Sum('n_versions'))['n_versions__sum'])
n_upstream = n_versions - n_packaged - n_overlay
pylab.figure(1, figsize=(3.5,3.5))
if n_overlay:
labels = 'Gentoo', 'Overlays', 'Upstream'
fracs = [n_packaged, n_overlay, n_upstream]
colors = '#008000', '#0B17FD', '#FF0000'
else:
labels = 'Gentoo', 'Upstream'
fracs = [n_packaged, n_upstream]
colors = '#008000', '#FF0000'
pylab.pie(fracs, labels=labels, colors=colors, autopct='%1.1f%%', shadow=True)
pylab.title('Versions', bbox={'facecolor':'0.8', 'pad':5})
@cached_pylab_chart
def pie_packages(**kwargs):
n_packages = packages(**kwargs).count()
n_packages_uptodate_main = packages(**kwargs).filter(n_versions=F('n_packaged')).count()
n_packages_uptodate_all = packages(**kwargs).filter(n_versions=F('n_packaged') + F('n_overlay')).count()
n_packages_outdated = n_packages - n_packages_uptodate_all
n_packages_uptodate_ovl = n_packages_uptodate_all - n_packages_uptodate_main
pylab.figure(1, figsize=(3.5,3.5))
if n_packages_uptodate_ovl:
labels = 'Ok (gentoo)', 'Ok (overlays)', 'Outdated'
fracs = [n_packages_uptodate_main, n_packages_uptodate_ovl, n_packages_outdated]
colors = '#008000', '#0B17FD', '#FF0000'
else:
labels = 'Ok (gentoo)', 'Outdated'
fracs = [n_packages_uptodate_main, n_packages_outdated]
colors = '#008000', '#FF0000'
pylab.pie(fracs, labels=labels, colors=colors, autopct='%1.1f%%', shadow=True)
pylab.title('Packages', bbox={'facecolor':'0.8', 'pad':5})

View File

@ -62,7 +62,7 @@ class Command(BaseCommand):
matches = sorted(matches) matches = sorted(matches)
pkg = matches.pop() pkg = matches.pop()
if pkg.version == '9999' and len(matches): if '9999' in pkg.version and len(matches):
pkg = matches.pop() pkg = matches.pop()
obj, created = Package.objects.get_or_create(category=pkg.category, name=pkg.name) obj, created = Package.objects.get_or_create(category=pkg.category, name=pkg.name)

View File

@ -103,14 +103,14 @@ class Command(BaseCommand):
slot = match.group('slot') slot = match.group('slot')
overlay = match.group('overlay') overlay = match.group('overlay')
if not package or not cpv.startswith(str(package)): cat, pkg, ver, rev = portage.catpkgsplit(cpv)
package = self.store_package(options, cpv)
if not package or not (cat == package.category and pkg == package.name):
package = self.store_package(options, cat, pkg)
self.store_version(options, package, cpv, slot, overlay) self.store_version(options, package, cpv, slot, overlay)
def store_package(self, options, cpv): def store_package(self, options, cat, pkg):
cat, pkg, ver, rev = portage.catpkgsplit(cpv)
obj, created = Package.objects.get_or_create(category=cat, name=pkg) obj, created = Package.objects.get_or_create(category=cat, name=pkg)
if created: if created:
@ -121,6 +121,7 @@ class Command(BaseCommand):
Version.objects.filter(package=obj, packaged=True).delete() Version.objects.filter(package=obj, packaged=True).delete()
obj.n_packaged = 0 obj.n_packaged = 0
obj.n_overlay = 0
obj.n_versions = Version.objects.filter(package=obj).count() obj.n_versions = Version.objects.filter(package=obj).count()
obj.save() obj.save()
@ -144,7 +145,10 @@ class Command(BaseCommand):
overlay=overlay) overlay=overlay)
if created or not package.n_packaged: if created or not package.n_packaged:
if overlay == 'gentoo':
package.n_packaged += 1 package.n_packaged += 1
else:
package.n_overlay += 1
if created: if created:
package.n_versions += 1 package.n_versions += 1

View File

@ -51,40 +51,44 @@ class Command(BaseCommand):
for package in Package.objects.all(): for package in Package.objects.all():
# Should not be needed, but can't hurt # Should not be needed, but can't hurt
package.n_versions = Version.objects.filter(package=package).count() package.n_versions = Version.objects.filter(package=package).count()
package.n_packaged != Version.objects.filter(package=package,packaged=True).count() package.n_packaged = Version.objects.filter(package=package, packaged=True, overlay='gentoo').count()
package.n_overlay = Version.objects.filter(package=package, packaged=True).exclude(overlay='gentoo').count()
package.save() package.save()
for herd in package.herds.all(): for herd in package.herds.all():
herds[herd].n_packages += 1 herds[herd].n_packages += 1
herds[herd].n_versions += package.n_versions herds[herd].n_versions += package.n_versions
herds[herd].n_packaged += package.n_packaged herds[herd].n_packaged += package.n_packaged
herds[herd].n_overlay += package.n_overlay
for maintainer in package.maintainers.all(): for maintainer in package.maintainers.all():
maintainers[maintainer].n_packages += 1 maintainers[maintainer].n_packages += 1
maintainers[maintainer].n_versions += package.n_versions maintainers[maintainer].n_versions += package.n_versions
maintainers[maintainer].n_packaged += package.n_packaged maintainers[maintainer].n_packaged += package.n_packaged
maintainers[maintainer].n_overlay += package.n_overlay
categories[package.category].n_packages += 1 categories[package.category].n_packages += 1
categories[package.category].n_versions += package.n_versions categories[package.category].n_versions += package.n_versions
categories[package.category].n_packaged += package.n_packaged categories[package.category].n_packaged += package.n_packaged
categories[package.category].n_overlay += package.n_overlay
for clog in categories.values(): for clog in categories.values():
if not options['quiet']: if not options['quiet']:
self.stdout.write('[c] %s - [%d, %d/%d]\n' % self.stdout.write('[c] %s - [%d, %d/%d/%d]\n' %
(clog.category, clog.n_packages, (clog.category, clog.n_packages,
clog.n_packaged, clog.n_versions)) clog.n_packaged, clog.n_overlay, clog.n_versions))
clog.save() clog.save()
for hlog in herds.values(): for hlog in herds.values():
if not options['quiet']: if not options['quiet']:
self.stdout.write('[h] %s - [%d, %d/%d]\n' % self.stdout.write('[h] %s - [%d, %d/%d/%d]\n' %
(hlog.herd, hlog.n_packages, (hlog.herd, hlog.n_packages,
hlog.n_packaged, hlog.n_versions)) hlog.n_packaged, hlog.n_overlay, hlog.n_versions))
hlog.save() hlog.save()
for mlog in maintainers.values(): for mlog in maintainers.values():
if not options['quiet']: if not options['quiet']:
self.stdout.write('[m] %s - [%d, %d/%d]\n' % self.stdout.write('[m] %s - [%d, %d/%d/%d]\n' %
(mlog.maintainer, mlog.n_packages, (mlog.maintainer, mlog.n_packages,
mlog.n_packaged, mlog.n_versions)) mlog.n_packaged, mlog.n_overlay, mlog.n_versions))
mlog.save() mlog.save()

View File

@ -0,0 +1,115 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'CategoryLog.n_overlay'
db.add_column('euscan_categorylog', 'n_overlay', self.gf('django.db.models.fields.IntegerField')(default=0), keep_default=False)
# Adding field 'Package.n_overlay'
db.add_column('euscan_package', 'n_overlay', self.gf('django.db.models.fields.IntegerField')(default=0), keep_default=False)
# Adding field 'HerdLog.n_overlay'
db.add_column('euscan_herdlog', 'n_overlay', self.gf('django.db.models.fields.IntegerField')(default=0), keep_default=False)
# Adding field 'MaintainerLog.n_overlay'
db.add_column('euscan_maintainerlog', 'n_overlay', self.gf('django.db.models.fields.IntegerField')(default=0), keep_default=False)
def backwards(self, orm):
# Deleting field 'CategoryLog.n_overlay'
db.delete_column('euscan_categorylog', 'n_overlay')
# Deleting field 'Package.n_overlay'
db.delete_column('euscan_package', 'n_overlay')
# Deleting field 'HerdLog.n_overlay'
db.delete_column('euscan_herdlog', 'n_overlay')
# Deleting field 'MaintainerLog.n_overlay'
db.delete_column('euscan_maintainerlog', 'n_overlay')
models = {
'euscan.categorylog': {
'Meta': {'object_name': 'CategoryLog'},
'category': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'datetime': ('django.db.models.fields.DateTimeField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'n_overlay': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'n_packaged': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'n_packages': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'n_versions': ('django.db.models.fields.IntegerField', [], {'default': '0'})
},
'euscan.euscanresult': {
'Meta': {'object_name': 'EuscanResult'},
'datetime': ('django.db.models.fields.DateTimeField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'package': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['euscan.Package']"}),
'result': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
'euscan.herd': {
'Meta': {'object_name': 'Herd'},
'email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
'herd': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'euscan.herdlog': {
'Meta': {'object_name': 'HerdLog'},
'datetime': ('django.db.models.fields.DateTimeField', [], {}),
'herd': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['euscan.Herd']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'n_overlay': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'n_packaged': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'n_packages': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'n_versions': ('django.db.models.fields.IntegerField', [], {'default': '0'})
},
'euscan.maintainer': {
'Meta': {'object_name': 'Maintainer'},
'email': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
},
'euscan.maintainerlog': {
'Meta': {'object_name': 'MaintainerLog'},
'datetime': ('django.db.models.fields.DateTimeField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'maintainer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['euscan.Maintainer']"}),
'n_overlay': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'n_packaged': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'n_packages': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'n_versions': ('django.db.models.fields.IntegerField', [], {'default': '0'})
},
'euscan.package': {
'Meta': {'unique_together': "(['category', 'name'],)", 'object_name': 'Package'},
'category': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'herds': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['euscan.Herd']", 'symmetrical': 'False', 'blank': 'True'}),
'homepage': ('django.db.models.fields.CharField', [], {'max_length': '256', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'maintainers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['euscan.Maintainer']", 'symmetrical': 'False', 'blank': 'True'}),
'n_overlay': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'n_packaged': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'n_versions': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
},
'euscan.version': {
'Meta': {'unique_together': "(['package', 'slot', 'revision', 'version', 'overlay'],)", 'object_name': 'Version'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'overlay': ('django.db.models.fields.CharField', [], {'default': "'gentoo'", 'max_length': '128'}),
'package': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['euscan.Package']"}),
'packaged': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'revision': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'slot': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'urls': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'version': ('django.db.models.fields.CharField', [], {'max_length': '128'})
}
}
complete_apps = ['euscan']

View File

@ -27,6 +27,7 @@ class Package(models.Model):
# For performance, we keep pre-computed counters # For performance, we keep pre-computed counters
n_versions = models.IntegerField(default=0) n_versions = models.IntegerField(default=0)
n_packaged = models.IntegerField(default=0) n_packaged = models.IntegerField(default=0)
n_overlay = models.IntegerField(default=0)
def __unicode__(self): def __unicode__(self):
return '%s/%s' % (self.category, self.name) return '%s/%s' % (self.category, self.name)
@ -40,7 +41,7 @@ class Version(models.Model):
revision = models.CharField(max_length=128) revision = models.CharField(max_length=128)
version = models.CharField(max_length=128) version = models.CharField(max_length=128)
packaged = models.BooleanField() packaged = models.BooleanField()
overlay = models.CharField(max_length=128) overlay = models.CharField(max_length=128, default='gentoo')
urls = models.TextField(blank=True) urls = models.TextField(blank=True)
def __unicode__(self): def __unicode__(self):
@ -64,6 +65,7 @@ class CategoryLog(models.Model):
n_packages = models.IntegerField(default=0) n_packages = models.IntegerField(default=0)
n_versions = models.IntegerField(default=0) n_versions = models.IntegerField(default=0)
n_packaged = models.IntegerField(default=0) n_packaged = models.IntegerField(default=0)
n_overlay = models.IntegerField(default=0)
class HerdLog(models.Model): class HerdLog(models.Model):
herd = models.ForeignKey(Herd) herd = models.ForeignKey(Herd)
@ -72,6 +74,7 @@ class HerdLog(models.Model):
n_packages = models.IntegerField(default=0) n_packages = models.IntegerField(default=0)
n_versions = models.IntegerField(default=0) n_versions = models.IntegerField(default=0)
n_packaged = models.IntegerField(default=0) n_packaged = models.IntegerField(default=0)
n_overlay = models.IntegerField(default=0)
class MaintainerLog(models.Model): class MaintainerLog(models.Model):
maintainer = models.ForeignKey(Maintainer) maintainer = models.ForeignKey(Maintainer)
@ -80,3 +83,4 @@ class MaintainerLog(models.Model):
n_packages = models.IntegerField(default=0) n_packages = models.IntegerField(default=0)
n_versions = models.IntegerField(default=0) n_versions = models.IntegerField(default=0)
n_packaged = models.IntegerField(default=0) n_packaged = models.IntegerField(default=0)
n_overlay = models.IntegerField(default=0)

View File

@ -0,0 +1,8 @@
from django import template
register = template.Library()
def div(value, arg=None):
return value/arg
register.filter('div', div)

View File

@ -5,3 +5,11 @@ register = template.Library()
@register.inclusion_tag('euscan/_packages.html') @register.inclusion_tag('euscan/_packages.html')
def packages(packages): def packages(packages):
return { 'packages' : packages } return { 'packages' : packages }
@register.inclusion_tag('euscan/_package_cols.html')
def package_cols(infos):
return { 'infos' : infos }
@register.inclusion_tag('euscan/_package_bar.html')
def package_bar(infos):
return { 'infos' : infos }

View File

@ -2,14 +2,19 @@ from django.conf.urls.defaults import *
urlpatterns = patterns('euscan.views', urlpatterns = patterns('euscan.views',
(r'^$', 'index'), (r'^$', 'index'),
(r'^logs/$', 'logs'), (r'^about/$', 'about'),
(r'^statistics/$', 'statistics'),
(r'^statistics/charts/(?P<chart>[\w\-]+).png$', 'chart'),
(r'^world/$', 'world'), (r'^world/$', 'world'),
(r'^world/scan/$', 'world_scan'), (r'^world/scan/$', 'world_scan'),
(r'^categories/$', 'categories'), (r'^categories/$', 'categories'),
(r'^categories/(?P<category>[\w+][\w+.-]*)/view/$', 'category'), (r'^categories/(?P<category>[\w+][\w+.-]*)/view/$', 'category'),
(r'^categories/(?P<category>[\w+][\w+.-]*)/charts/(?P<chart>[\w\-]+).png$', 'chart_category'),
(r'^herds/$', 'herds'), (r'^herds/$', 'herds'),
(r'^herds/(?P<herd>[\{\}\w+.-]*)/view/$', 'herd'), (r'^herds/(?P<herd>[\{\}\w+.-]*)/view/$', 'herd'),
(r'^herds/(?P<herd>[\{\}\w+.-]*)/charts/(?P<chart>[\w\-]+).png$', 'chart_herd'),
(r'^maintainers/$', 'maintainers'), (r'^maintainers/$', 'maintainers'),
(r'^maintainers/(?P<maintainer_id>\d+)/view/$', 'maintainer'), (r'^maintainers/(?P<maintainer_id>\d+)/view/$', 'maintainer'),
(r'^maintainers/(?P<maintainer_id>\d+)/charts/(?P<chart>[\w\-]+).png$', 'chart_maintainer'),
(r'^package/(?P<category>[\w+][\w+.-]*)/(?P<package>[\w+][\w+.-]*)/$', 'package'), (r'^package/(?P<category>[\w+][\w+.-]*)/(?P<package>[\w+][\w+.-]*)/$', 'package'),
) )

View File

@ -1,4 +1,5 @@
from annoying.decorators import render_to from annoying.decorators import render_to
from django.http import HttpResponse
from django.http import Http404 from django.http import Http404
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.db.models import Sum, Max from django.db.models import Sum, Max
@ -6,13 +7,17 @@ from django.db.models import Sum, Max
from euscan.models import Version, Package, Herd, Maintainer, EuscanResult from euscan.models import Version, Package, Herd, Maintainer, EuscanResult
from euscan.forms import WorldForm, PackagesForm from euscan.forms import WorldForm, PackagesForm
import charts
""" Views """
@render_to('euscan/index.html') @render_to('euscan/index.html')
def index(request): def index(request):
ctx = {} ctx = {}
ctx['n_packaged'] = Package.objects.aggregate(Sum('n_packaged'))['n_packaged__sum'] ctx['n_packaged'] = charts.xint(Package.objects.aggregate(Sum('n_packaged'))['n_packaged__sum'])
ctx['n_versions'] = Package.objects.aggregate(Sum('n_versions'))['n_versions__sum'] ctx['n_overlay'] = charts.xint(Package.objects.aggregate(Sum('n_overlay'))['n_overlay__sum'])
if ctx['n_versions'] is not None and ctx['n_packaged'] is not None: ctx['n_versions'] = charts.xint(Package.objects.aggregate(Sum('n_versions'))['n_versions__sum'])
ctx['n_upstream'] = ctx['n_versions'] - ctx['n_packaged'] ctx['n_upstream'] = ctx['n_versions'] - ctx['n_packaged'] - ctx['n_overlay']
ctx['n_packages'] = Package.objects.count() ctx['n_packages'] = Package.objects.count()
ctx['n_herds'] = Herd.objects.count() ctx['n_herds'] = Herd.objects.count()
ctx['n_maintainers'] = Maintainer.objects.count() ctx['n_maintainers'] = Maintainer.objects.count()
@ -25,7 +30,10 @@ def logs(request):
@render_to('euscan/categories.html') @render_to('euscan/categories.html')
def categories(request): def categories(request):
categories = Package.objects.values('category').annotate(n_packaged=Sum('n_packaged'), n_versions=Sum('n_versions')) categories = Package.objects.values('category').annotate(n_packaged=Sum('n_packaged'),
n_overlay=Sum('n_overlay'),
n_versions=Sum('n_versions'))
return { 'categories' : categories } return { 'categories' : categories }
@render_to('euscan/category.html') @render_to('euscan/category.html')
@ -38,7 +46,10 @@ def category(request, category):
@render_to('euscan/herds.html') @render_to('euscan/herds.html')
def herds(request): def herds(request):
# FIXME: optimize the query, it uses 'LEFT OUTER JOIN' instead of 'INNER JOIN' # FIXME: optimize the query, it uses 'LEFT OUTER JOIN' instead of 'INNER JOIN'
herds = Package.objects.filter(herds__isnull=False).values('herds__herd').annotate(n_packaged=Sum('n_packaged'), n_versions=Sum('n_versions')) herds = Package.objects.filter(herds__isnull=False)
herds = herds.values('herds__herd').annotate(n_packaged=Sum('n_packaged'),
n_overlay=Sum('n_overlay'),
n_versions=Sum('n_versions'))
return { 'herds' : herds } return { 'herds' : herds }
@render_to('euscan/herd.html') @render_to('euscan/herd.html')
@ -49,7 +60,12 @@ def herd(request, herd):
@render_to('euscan/maintainers.html') @render_to('euscan/maintainers.html')
def maintainers(request): def maintainers(request):
maintainers = Package.objects.filter(maintainers__isnull=False).values('maintainers__id', 'maintainers__name').annotate(n_packaged=Sum('n_packaged'), n_versions=Sum('n_versions')) maintainers = Package.objects.filter(maintainers__isnull=False)
maintainers = maintainers.values('maintainers__id', 'maintainers__name')
maintainers = maintainers.annotate(n_packaged=Sum('n_packaged'),
n_overlay=Sum('n_overlay'),
n_versions=Sum('n_versions'))
return { 'maintainers' : maintainers } return { 'maintainers' : maintainers }
@render_to('euscan/maintainer.html') @render_to('euscan/maintainer.html')
@ -61,6 +77,7 @@ def maintainer(request, maintainer_id):
@render_to('euscan/package.html') @render_to('euscan/package.html')
def package(request, category, package): def package(request, category, package):
package = get_object_or_404(Package, category=category, name=package) package = get_object_or_404(Package, category=category, name=package)
package.homepages = package.homepage.split(' ')
packaged = Version.objects.filter(package=package, packaged=True) packaged = Version.objects.filter(package=package, packaged=True)
upstream = Version.objects.filter(package=package, packaged=False) upstream = Version.objects.filter(package=package, packaged=False)
log = EuscanResult.objects.filter(package=package).order_by('-datetime')[:1] log = EuscanResult.objects.filter(package=package).order_by('-datetime')[:1]
@ -99,8 +116,42 @@ def world_scan(request):
packages.extend(Package.objects.filter(name=pkg)) packages.extend(Package.objects.filter(name=pkg))
except: except:
pass pass
print packages
return { 'packages' : packages } return { 'packages' : packages }
@render_to("euscan/about.html")
def about(request):
return {}
@render_to("euscan/statistics.html")
def statistics(request):
return {}
def chart(request, **kwargs):
from django.views.static import serve
chart = kwargs['chart'] if 'chart' in kwargs else None
if 'maintainer_id' in kwargs:
kwargs['maintainer'] = get_object_or_404(Maintainer, id=kwargs['maintainer_id'])
if 'herd' in kwargs:
kwargs['herd'] = get_object_or_404(Herd, herd=kwargs['herd'])
if chart == 'pie-packages':
path = charts.pie_packages(**kwargs)
elif chart == 'pie-versions':
path = charts.pie_versions(**kwargs)
else:
raise Http404()
return serve(request, path, document_root=charts.CHARTS_ROOT)
def chart_maintainer(request, **kwargs):
return chart(request, **kwargs)
def chart_herd(request, **kwargs):
return chart(request, **kwargs)
def chart_category(request, **kwargs):
return chart(request, **kwargs)

View File

View File

@ -148,10 +148,6 @@ h1 {
color:#15B100; color:#15B100;
} }
.bad {
color:#FFFFFF;
}
hr hr
{ {
margin: 0.3em 1em 0.3em 1em; margin: 0.3em 1em 0.3em 1em;
@ -230,3 +226,26 @@ th
clear: both; clear: both;
} }
.package_stat {
width: 40px;
float: right;
height: 5px;
border: 1px solid #ccc;
}
.package_stat div {
float: left;
height: 5px;
}
.package_stat .packaged {
background: #85ACFF;
}
.package_stat .overlay {
background: #C6D9FD;
}
.package_stat .upstream {
background: #FDEADD;
}

View File

@ -184,20 +184,12 @@ table.display td.center {
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* DataTables row classes * DataTables row classes
*/ */
table.display tr.gradeA { table.display td.bad {
background-color: #ddffdd; background-color: #FFDFDF;
} }
table.display tr.gradeC { table.display td.ugly {
background-color: #ddddff; background-color: #EDB9B9;
}
table.display tr.gradeX {
background-color: #ffdddd;
}
table.display tr.gradeU {
background-color: #ddd;
} }
tr { tr {
@ -335,22 +327,6 @@ table.KeyTable td.focus {
border: 3px solid #3366FF; border: 3px solid #3366FF;
} }
table.display tr.gradeA {
background-color: #eeffee;
}
table.display tr.gradeC {
background-color: #ddddff;
}
table.display tr.gradeX {
background-color: #ffdddd;
}
table.display tr.gradeU {
background-color: #ddd;
}
div.box { div.box {
height: 100px; height: 100px;
padding: 10px; padding: 10px;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -106,5 +106,6 @@ INSTALLED_APPS = (
'django.contrib.admin', 'django.contrib.admin',
# Uncomment the next line to enable admin documentation: # Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs', # 'django.contrib.admindocs',
'GChartWrapper.charts'
) )

View File

@ -25,26 +25,23 @@
<li><a href="{% url euscan.views.categories %}">Categories</a></li> <li><a href="{% url euscan.views.categories %}">Categories</a></li>
<li><a href="{% url euscan.views.herds %}">Herds</a></li> <li><a href="{% url euscan.views.herds %}">Herds</a></li>
<li><a href="{% url euscan.views.maintainers %}">Maintainers</a></li> <li><a href="{% url euscan.views.maintainers %}">Maintainers</a></li>
<li><a href="{% url euscan.views.world %}">World</a></li> <li><a href="{% url euscan.views.world %}">Scan World</a></li>
<li><a href="{% url euscan.views.statistics %}">Statistics</a></li>
<!--
<li>---</li> <li>---</li>
<li><a href="#">Login</a></li> <li><a href="#">Login</a></li>
<li><a href="#">Register</a></li> <li><a href="#">Register</a></li>
-->
<li>---</li> <li>---</li>
<li><a href="#">About</a></li> <li><a href="{% url euscan.views.about %}">About</a></li>
{% endblock %} {% endblock %}
</ul> </ul>
</div> </div>
{% endblock %} {% endblock %}
</div> </div>
<div id="footer"> <div id="footer">
Powered by: Questions, Comments, Corrections ?
<a href="http://kernel.org" titke="Linux Kernel"> Email: corentin.chary at gmail.com<br />
<img src="{{MEDIA_URL}}img/linux.png" alt="Linux" />
</a>
<a href="http://gentoo.org" title="Gentoo">
<img src="{{MEDIA_URL}}img/gentoo.png" alt="Gentoo Linux" />
</a>
-
Copyright (C) 2011 <strong>Corentin Chary</strong> Copyright (C) 2011 <strong>Corentin Chary</strong>
</div> </div>
</body> </body>

View File

@ -0,0 +1,9 @@
{% load mul %}
{% load sub %}
{% load div %}
<div class="package_stat">
<div class="packaged" style="width: {{ infos.n_packaged|mul:100.0|div:infos.n_versions }}%"></div>
<div class="overlay" style="width: {{ infos.n_overlay|mul:100.0|div:infos.n_versions }}%"></div>
<div class="upstream" style="width: {{ infos.n_versions|sub:infos.n_overlay|sub:infos.n_packaged|mul:100.0|div:infos.n_versions }}%"></div>
</div>

View File

@ -0,0 +1,24 @@
{% load packages %}
{% load mul %}
{% load sub %}
{% load div %}
<td>{{ infos.n_packaged }}</td>
{% if infos.n_overlay == 0 or infos.n_overlay <= infos.n_packaged %}
<td>
{% else %}{% if infos.n_overlay < infos.n_packaged %}
<td class="bad">
{% else %}
<td class="ugly">
{% endif %}{% endif %}
{{ infos.n_overlay }}
</td>
{% if infos.n_versions == infos.n_packaged|add:infos.n_overlay %}
<td>
{% else %}{% if infos.n_versions < infos.n_packaged|add:infos.n_overlay|mul:2 %}
<td class="bad">
{% else %}
<td class="ugly">
{% endif %}{% endif %}
{{ infos.n_versions|sub:infos.n_packaged|sub:infos.n_overlay }}
</td>

View File

@ -1,28 +1,22 @@
{% load sub %} {% load packages %}
{% load mul %}
<table id="table" class="display"> <table id="table" class="display">
<thead> <thead>
<th>Package</th> <th>Package</th>
<th>Ebuilds</th> <th>Gentoo</th>
<th>Overlays</th>
<th>Unpackaged</th> <th>Unpackaged</th>
</thead> </thead>
<tbody> <tbody>
{% for package in packages %} {% for package in packages %}
{% if package.n_versions == package.n_packaged %} <tr>
<tr class="gradeA">
{% else %}{% if package.n_versions < package.n_packaged|mul:2 %}
<tr class="gradeC">
{% else %}
<tr class="gradeX">
{% endif %}{% endif %}
<td> <td>
<a href="{% url euscan.views.package package.category package.name %}"> <a href="{% url euscan.views.package package.category package.name %}">
{{ package.category }}/{{ package.name }} {{ package.category }}/{{ package.name }}
</a> </a>
{% package_bar package %}
</td> </td>
<td>{{ package.n_packaged }}</td> {% package_cols package %}
<td>{{ package.n_versions|sub:package.n_packaged }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@ -0,0 +1,37 @@
{% extends "_base.html" %}
{% block content %}
<h2>What's euscan ?</h2>
<p>
Euscan is both a tool to check if an ebuild is outdated (app-portage/euscan) and a web interface.
</p>
<p>
This web interface allow you to browse the portage tree, and find outdated ebuilds. It is designed to help maintainers monitor their packages and bump them.
</p>
<p>
euscan code source is available at <a href="http://git.iksaif.net/?p=euscan.git;a=summary">http://git.iksaif.net/?p=euscan.git;a=summary</a>.
</p>
<p>
<em>This site is not an official Gentoo website.</em>
</p>
<p>
Copyright (C) 2011 <strong>Corentin Chary</strong>
</p>
<p>
Powered by:
<a href="http://kernel.org" titke="Linux Kernel">
<img src="{{MEDIA_URL}}img/linux.png" alt="Linux Kernel" />
</a>
<a href="http://gentoo.org" title="Gentoo">
<img src="{{MEDIA_URL}}img/gentoo.png" alt="Gentoo Linux" />
</a>
<a href="http://www.djangoproject.com/" title="Django">
<img src="{{MEDIA_URL}}img/django.png" alt="Django" />
</a>
<a href="http://python.org" title="Python">
<img src="{{MEDIA_URL}}img/python.png" alt="Python" />
</a>
</p>
{% endblock %}

View File

@ -1,7 +1,6 @@
{% extends "euscan/_datatable.html" %} {% extends "euscan/_datatable.html" %}
{% load sub %} {% load packages %}
{% load mul %}
{% block title %} {% block title %}
{{ block.super }} - categories {{ block.super }} - categories
@ -9,24 +8,22 @@
{% block content %} {% block content %}
<h2>Categories</h2> <h2>Categories</h2>
<table id="table" class="display"> <table id="table" class="display">
<thead> <thead>
<th>Category</th> <th>Category</th>
<th>Ebuilds</th> <th>Gentoo</th>
<th>Overlays</th>
<th>Unpackaged</th> <th>Unpackaged</th>
</thead> </thead>
<tbody> <tbody>
{% for category in categories %} {% for category in categories %}
{% if category.n_versions == category.n_packaged %} <tr>
<tr class="gradeA"> <td>
{% else %}{% if category.n_versions < category.n_packaged|mul:1.5 %} <a href="{% url euscan.views.category category.category %}">{{ category.category }}</a>
<tr class="gradeC"> {% package_bar category %}
{% else %} </td>
<tr class="gradeX"> {% package_cols category %}
{% endif %}{% endif %}
<td><a href="{% url euscan.views.category category.category %}">{{ category.category }}</a></td>
<td>{{ category.n_packaged }}</td>
<td>{{ category.n_versions|sub:category.n_packaged }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@ -9,4 +9,9 @@
{% block content %} {% block content %}
<h2>Category: {{ category }}</h2> <h2>Category: {{ category }}</h2>
{% packages packages %} {% packages packages %}
<h3>Statistics</h2>
<h4>Current statistics</h4>
<img src="{% url euscan.views.chart_category category 'pie-versions' %}" />
<img src="{% url euscan.views.chart_category category 'pie-packages' %}" />
{% endblock %} {% endblock %}

View File

@ -9,4 +9,9 @@
{% block content %} {% block content %}
<h2>Herd: {{ herd.herd }}</h2> <h2>Herd: {{ herd.herd }}</h2>
{% packages packages %} {% packages packages %}
<h3>Statistics</h2>
<h4>Current statistics</h4>
<img src="{% url euscan.views.chart_herd herd.herd 'pie-versions' %}" />
<img src="{% url euscan.views.chart_herd herd.herd 'pie-packages' %}" />
{% endblock %} {% endblock %}

View File

@ -1,7 +1,6 @@
{% extends "euscan/_datatable.html" %} {% extends "euscan/_datatable.html" %}
{% load sub %} {% load packages %}
{% load mul %}
{% block title %} {% block title %}
{{ block.super }} - herds {{ block.super }} - herds
@ -12,25 +11,20 @@
<table id="table" class="display"> <table id="table" class="display">
<thead> <thead>
<th>Herd</th> <th>Herd</th>
<th>Ebuilds</th> <th>Gentoo</th>
<th>Overlays</th>
<th>Unpackaged</th> <th>Unpackaged</th>
</thead> </thead>
<tbody> <tbody>
{% for herd in herds %} {% for herd in herds %}
{% if herd.n_versions == herd.n_packaged %} <tr>
<tr class="gradeA">
{% else %}{% if herd.n_versions < herd.n_packaged|mul:2 %}
<tr class="gradeC">
{% else %}
<tr class="gradeX">
{% endif %}{% endif %}
<td> <td>
<a href="{% url euscan.views.herd herd.herds__herd %}"> <a href="{% url euscan.views.herd herd.herds__herd %}">
{{ herd.herds__herd }} {{ herd.herds__herd }}
</a> </a>
{% package_bar herd %}
</td> </td>
<td>{{ herd.n_packaged }}</td> {% package_cols herd %}
<td>{{ herd.n_versions|sub:herd.n_packaged }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@ -8,12 +8,10 @@
Euscan is both a tool to check if an ebuild is outdated (app-portage/euscan) and a web interface. Euscan is both a tool to check if an ebuild is outdated (app-portage/euscan) and a web interface.
</p> </p>
<p> <p>
This web interface allow you to browse the portage tree, and find outdated ebuilds. It was designed to help maintainers monitor their packages and bump them. This web interface allow you to browse the portage tree, and find outdated ebuilds. It is designed to help maintainers monitor their packages and bump them.
</p> </p>
<p>
euscan code source is available at <a href="http://git.iksaif.net/?p=euscan.git;a=summary">http://git.iksaif.net/?p=euscan.git;a=summary</a>. <h2>Overview</h2>
</p>
<h2>Statistics</h2>
<ul> <ul>
<li>Packages: {{ n_packages }}</li> <li>Packages: {{ n_packages }}</li>
<li>Versions: {{ n_packaged }}</li> <li>Versions: {{ n_packaged }}</li>

View File

@ -9,4 +9,10 @@
{% block content %} {% block content %}
<h2>Maintainer: {{ maintainer.name }} &lt{{ maintainer.email }}&gt</h2> <h2>Maintainer: {{ maintainer.name }} &lt{{ maintainer.email }}&gt</h2>
{% packages packages %} {% packages packages %}
<h3>Statistics</h2>
<h4>Current statistics</h4>
<img src="{% url euscan.views.chart_maintainer maintainer.id 'pie-versions' %}" />
<img src="{% url euscan.views.chart_maintainer maintainer.id 'pie-packages' %}" />
{% endblock %} {% endblock %}

View File

@ -1,7 +1,6 @@
{% extends "euscan/_datatable.html" %} {% extends "euscan/_datatable.html" %}
{% load sub %} {% load packages %}
{% load mul %}
{% block title %} {% block title %}
{{ block.super }} - maintainers {{ block.super }} - maintainers
@ -12,25 +11,20 @@
<table id="table" class="display"> <table id="table" class="display">
<thead> <thead>
<th>Maintainer</th> <th>Maintainer</th>
<th>Ebuilds</th> <th>Gentoo</th>
<th>Overlays</th>
<th>Unpackaged</th> <th>Unpackaged</th>
</thead> </thead>
<tbody> <tbody>
{% for maintainer in maintainers %} {% for maintainer in maintainers %}
{% if maintainer.n_versions == maintainer.n_packaged %} <tr>
<tr class="gradeA">
{% else %}{% if maintainer.n_versions < maintainer.n_packaged|mul:2 %}
<tr class="gradeC">
{% else %}
<tr class="gradeX">
{% endif %}{% endif %}
<td> <td>
<a href="{% url euscan.views.maintainer maintainer.maintainers__id %}"> <a href="{% url euscan.views.maintainer maintainer.maintainers__id %}">
{{ maintainer.maintainers__name }} {{ maintainer.maintainers__name }}
</a> </a>
{% package_bar maintainer %}
</td> </td>
<td>{{ maintainer.n_packaged }}</td> {% package_cols maintainer %}
<td>{{ maintainer.n_versions|sub:maintainer.n_packaged }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@ -17,7 +17,7 @@
<dt>Homepage</dt> <dt>Homepage</dt>
<dd> <dd>
{% for homepage in package.homepages %} {% for homepage in package.homepages %}
<a href="{{ homepage }}">{{ homepage }}<br /> <a href="{{ homepage }}">{{ homepage }}</a><br />
{% endfor %} {% endfor %}
</dd> </dd>
{% endif %} {% endif %}

View File

@ -0,0 +1,13 @@
{% extends "_base.html" %}
{% block content %}
<h2>What's euscan ?</h2>
<h2>Statistics</h2>
<h3>Current statistics</h3>
<img src="{% url euscan.views.chart 'pie-versions' %}" />
<img src="{% url euscan.views.chart 'pie-packages' %}" />
<h3>All time statistics</h3>
{% endblock %}