some refactoring, added Package manager for removing code duplicates, added helpers module, basic tests layout

This commit is contained in:
volpino
2012-05-01 16:56:09 +02:00
parent 1c53c60eed
commit 8e37f6249c
34 changed files with 260 additions and 84 deletions

View File

@ -8,19 +8,14 @@ from django.forms.models import model_to_dict
from djeuscan.models import Version, Package, Herd, Maintainer, EuscanResult, \
VersionLog
from djeuscan.helpers import xint
# replace default XMLEmitter with ours
from piston.emitters import Emitter
from emitters import EuscanXMLEmitter
Emitter.register('xml', EuscanXMLEmitter, 'text/xml; charset=utf-8')
def xint(i):
try:
return int(i)
except:
return 0
def renameFields(vqs, fields):
ret = []
for n in vqs:

View File

@ -1,14 +1,15 @@
import os.path
import time
from euscanwww import settings
import rrdtool
import pylab
from django.db.models import F, Sum
from euscanwww import settings
from djeuscan.models import Package
from djeuscan.helpers import xint
import rrdtool
import pylab
CHARTS_ROOT = os.path.join(settings.EUSCAN_ROOT, 'var', 'charts')
CHARTS_URL = os.path.join(settings.EUSCAN_ROOT, 'var', 'charts')
@ -20,13 +21,6 @@ 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):

View File

@ -0,0 +1,10 @@
"""
djeuscan.helpers
"""
def xint(i):
try:
return int(i)
except Exception:
return 0

View File

@ -0,0 +1,56 @@
"""
djeuscan.managers
"""
from django.db import models
from djeuscan.helpers import xint
class PackageManager(models.Manager):
def n_packaged(self):
res = self.aggregate(models.Sum('n_packaged'))['n_packaged__sum']
return xint(res)
def n_overlay(self):
res = self.aggregate(models.Sum('n_overlay'))['n_overlay__sum']
return xint(res)
def n_versions(self):
res = self.aggregate(models.Sum('n_versions'))['n_versions__sum']
return xint(res)
def n_upstream(self):
return self.n_versions() - self.n_packaged() - self.n_overlay()
def categories(self):
return self.values('category').annotate(
n_packaged=models.Sum('n_packaged'),
n_overlay=models.Sum('n_overlay'),
n_versions=models.Sum('n_versions')
)
def herds(self):
# FIXME: optimize the query, it uses 'LEFT OUTER JOIN' instead of
# 'INNER JOIN'
res = self.filter(herds__isnull=False)
res = res.values('herds__herd').annotate(
n_packaged=models.Sum('n_packaged'),
n_overlay=models.Sum('n_overlay'),
n_versions=models.Sum('n_versions')
)
return res
def maintainers(self):
res = self.filter(maintainers__isnull=False).values(
'maintainers__id', 'maintainers__name', 'maintainers__email'
)
res = res.annotate(
n_packaged=models.Sum('n_packaged'),
n_overlay=models.Sum('n_overlay'),
n_versions=models.Sum('n_versions')
)
return res
def overlays(self):
res = self.values('version__overlay').exclude(version__overlay='')
return res.distinct()

View File

@ -1,7 +1,13 @@
from django.db import models
from djeuscan.managers import PackageManager
class Herd(models.Model):
"""
A herd is a collection of packages
"""
herd = models.CharField(max_length=128, unique=True)
email = models.CharField(max_length=128, blank=True, null=True)
@ -12,6 +18,10 @@ class Herd(models.Model):
class Maintainer(models.Model):
"""
The person who maintains a package
"""
name = models.CharField(max_length=128)
email = models.CharField(max_length=128, unique=True)
@ -20,6 +30,10 @@ class Maintainer(models.Model):
class Package(models.Model):
"""
A portage package
"""
category = models.CharField(max_length=128)
name = models.CharField(max_length=128)
description = models.TextField(blank=True)
@ -27,12 +41,12 @@ class Package(models.Model):
herds = models.ManyToManyField(Herd, blank=True)
maintainers = models.ManyToManyField(Maintainer, blank=True)
' For performance, we keep pre-computed counters '
# For performance, we keep pre-computed counters
n_versions = models.IntegerField(default=0)
n_packaged = models.IntegerField(default=0)
n_overlay = models.IntegerField(default=0)
' And we also pre-compute last versions '
# And we also pre-compute last versions
last_version_gentoo = models.ForeignKey(
'Version', blank=True, null=True, related_name="last_version_gentoo",
on_delete=models.SET_NULL
@ -46,6 +60,8 @@ class Package(models.Model):
on_delete=models.SET_NULL
)
objects = PackageManager()
def __unicode__(self):
return '%s/%s' % (self.category, self.name)
@ -54,6 +70,10 @@ class Package(models.Model):
class Version(models.Model):
"""
Version associated to a package
"""
package = models.ForeignKey(Package)
slot = models.CharField(max_length=128)
revision = models.CharField(max_length=128)
@ -110,22 +130,29 @@ class EuscanResult(models.Model):
result = models.TextField(blank=True)
# Keep data for charts
class Log(models.Model):
"""
Model used for keeping data for charts
"""
datetime = models.DateTimeField()
' Packages up to date in the main portage tree '
# Packages up to date in the main portage tree
n_packages_gentoo = models.IntegerField(default=0)
' Packages up to date in an overlay '
# Packages up to date in an overlay
n_packages_overlay = models.IntegerField(default=0)
' Packages outdated '
# Packages outdated
n_packages_outdated = models.IntegerField(default=0)
' Versions in the main portage tree '
# Versions in the main portage tree
n_versions_gentoo = models.IntegerField(default=0)
' Versions in overlays '
# Versions in overlays
n_versions_overlay = models.IntegerField(default=0)
' Upstream versions, not in the main tree or overlays '
# Upstream versions, not in the main tree or overlays
n_versions_upstream = models.IntegerField(default=0)
def __unicode__(self):

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">
<head>
<title>{% block title %}euscan{% endblock %}</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
{% block meta %}
<link rel="alternate" type="application/atom+xml" title="Global log" href="{% url global_feed %}" />
{% endblock %}
{% block css %}
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/style.css" media="screen" title="Normal" />
{% endblock %}
{% block javascript %}
{% endblock %}
</head>
<body>
<div id="header">
<a href="http://www.gentoo.org">
<img id="logo" src="{{ STATIC_URL }}img/gentoo_org.png" />
</a>
{% block header %}<h1>Ebuild Upstream Scanner (euscan)</h1>{% endblock %}
</div>
<div id="content">
{% block content %}{% endblock %}
</div>
<div id="menus">
{% block menus %}
<div class="menu">
<ul>
{% block menu %}
<li><a href="{% url djeuscan.views.index %}">Home</a></li>
<li><a href="{% url djeuscan.views.categories %}">Categories</a></li>
<li><a href="{% url djeuscan.views.herds %}">Herds</a></li>
<li><a href="{% url djeuscan.views.maintainers %}">Maintainers</a></li>
<li><a href="{% url djeuscan.views.overlays %}">Overlays</a></li>
<li><a href="{% url djeuscan.views.world %}">Scan World</a></li>
<li><a href="{% url djeuscan.views.statistics %}">Statistics</a></li>
<!--
<li>---</li>
<li><a href="#">Login</a></li>
<li><a href="#">Register</a></li>
-->
<li>---</li>
{% block menu_feed %}
<li>
<img src="{{ STATIC_URL }}img/feed.png" alt="feed" />
<a title="Global Feed" href="{% url global_feed %}">Global Feed</a>
</li>
{% endblock %}
<li>---</li>
<li><a href="{% url djeuscan.views.api %}">API</a></li>
<li><a href="{% url djeuscan.views.about %}">About</a></li>
{% endblock %}
</ul>
</div>
{% endblock %}
</div>
<div id="footer">
<p>
Questions, Comments, Corrections ?
Email: corentin.chary at gmail.com<br />
Copyright (C) 2011 <strong>Corentin Chary</strong><br />
Original Gentoo artwork and logos copyright (C) Gentoo Foundation.<br />
Design inspired by (stolen from) gentoo.org and bugs.gentoo.org.<br />
<em>This site is not an official Gentoo website.</em>
</p>
</div>
</body>
</html>

View File

@ -0,0 +1,35 @@
{% extends "_base.html" %}
{% block css %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{{STATIC_URL}}css/table.css" media="screen" title="Normal" />
{% endblock %}
{% block javascript %}
{{ block.super }}
<script type="text/javascript" language="javascript" src="{{STATIC_URL}}js/jquery.js"></script>
<script type="text/javascript" language="javascript" src="{{STATIC_URL}}js/jquery.dataTables.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
$('#table').dataTable( {
"bPaginate": false,
"bInfo": false,
"bStateSave": true,
"sCookiePrefix": "datatables_"
} );
});
</script>
{% endblock %}
{% block menus %}
{{ block.super }}
<div class="menu">
<ul>
<li><img src="{{ STATIC_URL }}img/gentoo-icon.png" /> Gentoo</li>
<li><img src="{{ STATIC_URL }}img/overlay-icon.png" /> Overlays</li>
<li><img src="{{ STATIC_URL }}img/upstream-icon.png" /> Upstream</li>
<li><img src="{{ STATIC_URL }}img/freshness-icon.png" /> Freshness</li>
</ul>
</div>
{% endblock %}

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,30 @@
{% 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>
<td>
{{ infos.n_packaged|add:infos.n_overlay|div:infos.n_versions|mul:100|floatformat:"0" }}
</td>

View File

@ -0,0 +1,33 @@
{% load packages %}
{% load sub %}
{% load div %}
{% load mul %}
<table id="table" class="display">
<thead>
<th>Package</th>
<th><img src="{{ STATIC_URL }}img/gentoo-icon.png" alt="gentoo" title="Last version in Gentoo" /></th>
<th><img src="{{ STATIC_URL }}img/overlay-icon.png" alt="overlays" title="Last version in Overlays" /></th>
<th><img src="{{ STATIC_URL }}img/upstream-icon.png" alt="upstream" title="Last version Upstream" /></th>
<th><img src="{{ STATIC_URL }}img/gentoo-icon.png" alt="gentoo" title="Versions in Gentoo" /></th>
<th><img src="{{ STATIC_URL }}img/overlay-icon.png" alt="overlays" title="Versions in Overlays" /></th>
<th><img src="{{ STATIC_URL }}img/upstream-icon.png" alt="upstream" title="Versions only upstream" /></th>
<th><img src="{{ STATIC_URL }}img/freshness-icon.png" title="Freshness" /></th>
</thead>
<tbody>
{% for package in packages %}
<tr>
<td>
<a href="{% url djeuscan.views.package package.category package.name %}">
{{ package.category }}/{{ package.name }}
</a>
{% package_bar package %}
</td>
<td>{{ package.last_version_gentoo.version }}</td>
<td>{{ package.last_version_overlay.version }}</td>
<td>{{ package.last_version_upstream.version }}</td>
{% package_cols package %}
</tr>
{% endfor %}
</tbody>
</table>

View File

@ -0,0 +1,41 @@
{% 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=djeuscan.git;a=summary">http://git.iksaif.net/?p=euscan.git;a=summary</a>.
</p>
<p>
Original Gentoo artwork and logos copyright (C) Gentoo Foundation.
Design inspired by (stolen from) gentoo.org and bugs.gentoo.org.
</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="{{STATIC_URL}}img/linux.png" alt="Linux Kernel" />
</a>
<a href="http://gentoo.org" title="Gentoo">
<img src="{{STATIC_URL}}img/gentoo.png" alt="Gentoo Linux" />
</a>
<a href="http://www.djangoproject.com/" title="Django">
<img src="{{STATIC_URL}}img/django.png" alt="Django" />
</a>
<a href="http://python.org" title="Python">
<img src="{{STATIC_URL}}img/python.png" alt="Python" />
</a>
</p>
{% endblock %}

View File

@ -0,0 +1,22 @@
{% extends "_base.html" %}
{% block content %}
<h2>API</h2>
<p>
<ul>
<li><a href="{% url api.views.root 'json' %}">/api/1.0/api.json</a></li>
<li><a href="{% url api.views.statistics 'json' %}">/api/1.0/statistics.json</a></li>
<li><a href="{% url api.views.maintainers 'json' %}">/api/1.0/maintainers.json</a></li>
<li><a href="{% url api.views.herds 'json' %}">/api/1.0/herds.json</a></li>
<li><a href="{% url api.views.categories 'json' %}">/api/1.0/categories.json</a></li>
<li>/api/1.0/packages/by-maintainer/&lt;maintainer-id&gt;.json</li>
<li>/api/1.0/packages/by-category/&lt;category&gt;.json</li>
<li>/api/1.0/packages/by-herd/&lt;herd&gt;.json</li>
<li>/api/1.0/package/&lt;category&gt;/&lt;package&gt;.json</li>
</ul>
Formats: xml, yaml and json
</p>
{% endblock %}

View File

@ -0,0 +1,41 @@
{% extends "euscan/_datatable.html" %}
{% load packages %}
{% block title %}
{{ block.super }} - categories
{% endblock %}
{% block content %}
<h2>Categories</h2>
<table id="table" class="display">
<thead>
<th>Category</th>
<th><img src="{{ STATIC_URL }}img/gentoo-icon.png" alt="gentoo" title="Versions in Gentoo" /></th>
<th><img src="{{ STATIC_URL }}img/overlay-icon.png" alt="overlays" title="Versions in Overlays" /></th>
<th><img src="{{ STATIC_URL }}img/upstream-icon.png" alt="upstream" title="Versions only upstream" /></th>
<th><img src="{{ STATIC_URL }}img/freshness-icon.png" title="Freshness" /></th>
{% if request.GET.extras %}
<th>Graphs</th>
{% endif %}
</thead>
<tbody>
{% for category in categories %}
<tr>
<td>
<a href="{% url djeuscan.views.category category.category %}">{{ category.category }}</a>
{% package_bar category %}
</td>
{% package_cols category %}
{% if request.GET.extras %}
<td>
<img src="{% url djeuscan.views.chart_category category.category 'packages-monthly-small' %}" />
<img src="{% url djeuscan.views.chart_category category.category 'versions-monthly-small' %}" />
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -0,0 +1,53 @@
{% extends "euscan/_datatable.html" %}
{% load packages %}
{% block title %}
{{ block.super }} - Category: {{ category }}
{% endblock %}
{% block menu_feed %}
{{ block.super }}
<li>
<img src="{{ STATIC_URL }}/img/feed.png" alt="feed" />
<a title="{{ category }} Feed" href="{% url category_feed category %}">{{ category }}</a>
</li>
{% endblock %}
{% block content %}
<h2>Category: {{ category }}</h2>
{% packages packages %}
<h3>Statistics</h2>
<h4>Current statistics</h4>
<p>
<img src="{% url djeuscan.views.chart_category category 'pie-versions' %}" />
<img src="{% url djeuscan.views.chart_category category 'pie-packages' %}" />
</p>
<h4>All Time</h4>
<p>
<img src="{% url djeuscan.views.chart_category category 'versions-weekly' %}" />
<img src="{% url djeuscan.views.chart_category category 'versions-monthly' %}" />
</p>
<p>
<img src="{% url djeuscan.views.chart_category category 'packages-weekly' %}" />
<img src="{% url djeuscan.views.chart_category category 'packages-monthly' %}" />
</p>
{% endblock %}
{% block menus %}
{{ block.super }}
<div class="menu">
<dl>
<dt>Versions</dt>
<dd><img src="{% url djeuscan.views.chart_category category 'versions-monthly-small' %}" /></dd>
<dt>Packages</dt>
<dd><img src="{% url djeuscan.views.chart_category category 'packages-monthly-small' %}" /></dd>
</dl>
</div>
{% endblock %}

View File

@ -0,0 +1,51 @@
{% extends "euscan/_datatable.html" %}
{% load packages %}
{% block title %}
{{ block.super }} - Herd: {{ herd.herd }}
{% endblock %}
{% block menu_feed %}
{{ block.super }}
<li>
<img src="{{ STATIC_URL }}/img/feed.png" alt="feed" />
<a title="{{ herd.herd }} Feed" href="{% url herd_feed herd.herd %}">{{ herd.herd }}</a>
</li>
{% endblock %}
{% block content %}
<h2>Herd: {{ herd.herd }}</h2>
{% packages packages %}
<h3>Statistics</h2>
<h4>Current statistics</h4>
<p>
<img src="{% url djeuscan.views.chart_herd herd.herd 'pie-versions' %}" />
<img src="{% url djeuscan.views.chart_herd herd.herd 'pie-packages' %}" />
</p>
<h4>All Time</h4>
<p>
<img src="{% url djeuscan.views.chart_herd herd.herd 'versions-weekly' %}" />
<img src="{% url djeuscan.views.chart_herd herd.herd 'versions-monthly' %}" />
<p>
</p>
<img src="{% url djeuscan.views.chart_herd herd.herd 'packages-weekly' %}" />
<img src="{% url djeuscan.views.chart_herd herd.herd 'packages-monthly' %}" />
</p>
{% endblock %}
{% block menus %}
{{ block.super }}
<div class="menu">
<dl>
<dt>Versions</dt>
<dd><img src="{% url djeuscan.views.chart_herd herd.herd 'versions-monthly-small' %}" /></dd>
<dt>Packages</dt>
<dd><img src="{% url djeuscan.views.chart_herd herd.herd 'packages-monthly-small' %}" /></dd>
</dl>
</div>
{% endblock %}

View File

@ -0,0 +1,42 @@
{% extends "euscan/_datatable.html" %}
{% load packages %}
{% block title %}
{{ block.super }} - herds
{% endblock %}
{% block content %}
<h2>Herds</h2>
<table id="table" class="display">
<thead>
<th>Herd</th>
<th><img src="{{ STATIC_URL }}img/gentoo-icon.png" alt="gentoo" title="Versions in Gentoo" /></th>
<th><img src="{{ STATIC_URL }}img/overlay-icon.png" alt="overlays" title="Versions in Overlays" /></th>
<th><img src="{{ STATIC_URL }}img/upstream-icon.png" alt="upstream" title="Versions only upstream" /></th>
<th><img src="{{ STATIC_URL }}img/freshness-icon.png" title="Freshness" /></th>
{% if request.GET.extras %}
<th>Graphs</th>
{% endif %}
</thead>
<tbody>
{% for herd in herds %}
<tr>
<td>
<a href="{% url djeuscan.views.herd herd.herds__herd %}">
{{ herd.herds__herd }}
</a>
{% package_bar herd %}
</td>
{% package_cols herd %}
{% if request.GET.extras %}
<td>
<img src="{% url djeuscan.views.chart_herd herd.herds__herd 'packages-monthly-small' %}" />
<img src="{% url djeuscan.views.chart_herd herd.herds__herd 'versions-monthly-small' %}" />
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -0,0 +1,25 @@
{% extends "_base.html" %}
{% load timedelta %}
{% 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>
<h2>Overview</h2>
<ul>
<li>Packages: {{ n_packages }}</li>
<li>Versions: {{ n_packaged }}</li>
<li>Versions not-packaged: {{ n_upstream }}</li>
<li>Herds: {{ n_herds }}</li>
<li>Maintainers: {{ n_maintainers }}</li>
{% if last_scan %}
<li>Last scan: {{ last_scan }} ({{ last_scan|timedelta }})</li>
{% endif %}
</ul>
{% endblock %}

View File

@ -0,0 +1,52 @@
{% extends "euscan/_datatable.html" %}
{% load packages %}
{% block title %}
{{ block.super }} - Maintainer: {{ maintainer.name }}
{% endblock %}
{% block menu_feed %}
{{ block.super }}
<li>
<img src="{{ STATIC_URL }}/img/feed.png" alt="feed" />
<a title="{{ maintainer.name }} Feed" href="{% url maintainer_feed maintainer.id %}">{{ maintainer.name }}</a>
</li>
{% endblock %}
{% block content %}
<h2>Maintainer: {{ maintainer.name }} &lt{{ maintainer.email }}&gt</h2>
{% packages packages %}
<h3>Statistics</h2>
<h4>Current statistics</h4>
<p>
<img src="{% url djeuscan.views.chart_maintainer maintainer.id 'pie-versions' %}" />
<img src="{% url djeuscan.views.chart_maintainer maintainer.id 'pie-packages' %}" />
</p>
<h4>All Time</h4>
<p>
<img src="{% url djeuscan.views.chart_maintainer maintainer.id 'versions-weekly' %}" />
<img src="{% url djeuscan.views.chart_maintainer maintainer.id 'versions-monthly' %}" />
</p>
<p>
<img src="{% url djeuscan.views.chart_maintainer maintainer.id 'packages-weekly' %}" />
<img src="{% url djeuscan.views.chart_maintainer maintainer.id 'packages-monthly' %}" />
</p>
{% endblock %}
{% block menus %}
{{ block.super }}
<div class="menu">
<dl>
<dt>Versions</dt>
<dd><img src="{% url djeuscan.views.chart_maintainer maintainer.id 'versions-monthly-small' %}" /></dd>
<dt>Packages</dt>
<dd><img src="{% url djeuscan.views.chart_maintainer maintainer.id 'packages-monthly-small' %}" /></dd>
</dl>
</div>
{% endblock %}
5A

View File

@ -0,0 +1,46 @@
{% extends "euscan/_datatable.html" %}
{% load packages %}
{% block title %}
{{ block.super }} - maintainers
{% endblock %}
{% block content %}
<h2>Maintainers</h2>
<table id="table" class="display">
<thead>
<th>Maintainer</th>
<th><img src="{{ STATIC_URL }}img/gentoo-icon.png" alt="gentoo" title="Versions in Gentoo" /></th>
<th><img src="{{ STATIC_URL }}img/overlay-icon.png" alt="overlays" title="Versions in Overlays" /></th>
<th><img src="{{ STATIC_URL }}img/upstream-icon.png" alt="upstream" title="Versions only upstream" /></th>
<th><img src="{{ STATIC_URL }}img/freshness-icon.png" title="Freshness" /></th>
{% if request.GET.extras %}
<th>Graphs</th>
{% endif %}
</thead>
<tbody>
{% for maintainer in maintainers %}
<tr>
<td>
<a href="{% url djeuscan.views.maintainer maintainer.maintainers__id %}">
{% if maintainer.maintainers__name != maintainer.maintainers__email %}
{{ maintainer.maintainers__name }} &lt;{{ maintainer.maintainers__email }}&gt;
{% else %}
{{ maintainer.maintainers__name }}
{% endif %}
</a>
{% package_bar maintainer %}
</td>
{% package_cols maintainer %}
{% if request.GET.extras %}
<td>
<img src="{% url djeuscan.views.chart_maintainer maintainer.maintainers__id 'packages-monthly-small' %}" />
<img src="{% url djeuscan.views.chart_maintainer maintainer.maintainers__id 'versions-monthly-small' %}" />
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -0,0 +1,12 @@
{% extends "euscan/_datatable.html" %}
{% load packages %}
{% block title %}
{{ block.super }} - Overlay: {{ overlay }}
{% endblock %}
{% block content %}
<h2>Overlay: {{ overlay }}</h2>
{% packages packages %}
{% endblock %}

View File

@ -0,0 +1,26 @@
{% extends "euscan/_datatable.html" %}
{% load packages %}
{% block title %}
{{ block.super }} - overlays
{% endblock %}
{% block content %}
<h2>Overlays</h2>
<table id="table" class="display">
<thead>
<th>Overlay</th>
</thead>
<tbody>
{% for overlay in overlays %}
<tr>
<td>
<a href="{% url djeuscan.views.overlay overlay.version__overlay %}">{{ overlay.version__overlay }}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -0,0 +1,128 @@
{% extends "euscan/_datatable.html" %}
{% load sub %}
{% block meta %}
{{ block.super }}
<link rel="alternate" type="application/atom+xml" title="{{ package }} Feed" href="{% url package_feed package.category package.name %}" />
{% endblock %}
{% block menu_feed %}
{{ block.super }}
<li>
<img src="{{ STATIC_URL }}/img/feed.png" alt="feed" />
<a title="{{ package }} Feed" href="{% url package_feed package.category package.name %}">{{ package }}</a>
</li>
{% endblock %}
{% block title %}
{{ block.super }} - {{ package.category }}/{{ package.name }}
{% endblock %}
{% block content %}
<h2>{{ package.category }}/{{ package.name }}</h2>
<dl>
{% if package.description %}
<dt>Description</dt>
<dd>{{ package.description }}</dd>
{% endif %}
{% if package.homepage %}
<dt>Homepage</dt>
<dd>
{% for homepage in package.homepages %}
<a href="{{ homepage }}">{{ homepage }}</a><br />
{% endfor %}
</dd>
{% endif %}
{% if package.herds.all %}
<dt>Herds</dt>
<dd>
{% for herd in package.herds.all %}
<a href="{% url djeuscan.views.herd herd.herd %}">
{{ herd.herd }}
</a>
&lt;{{ herd.email }}&gt;
{% endfor %}
</dd>
{% endif %}
{% if package.maintainers.all %}
<dt>Maintainers</dt>
<dd>
{% for maintainer in package.maintainers.all %}
{% if maintainer.name != maintainer.email %}
<a href="{% url djeuscan.views.maintainer maintainer.id %}">
{{ maintainer.name }}
</a>
&lt;{{ maintainer.email }}&gt;
{% else %}
<a href="{% url djeuscan.views.maintainer maintainer.id %}">
{{ maintainer.email }}
</a>
{% endif %}
{% endfor %}
</dd>
{% endif %}
{% if packaged %}
<dt>Packaged Versions</dt>
<dd>
<ul>
{% for version in packaged %}
<li id="{{ version.version }}-{{version.revision }}:{{ version.slot }}-[{{ version.overlay }}]">
{% if version.overlay == "gentoo" %}
<img src="{{ STATIC_URL }}img/gentoo-icon.png" alt="gentoo" title="In Gentoo" />
{% else %}
<img src="{{ STATIC_URL }}img/overlay-icon.png" alt="overlays" title="In Overlays" />
{% endif %}
{{ version.version }}-{{ version.revision }} :{{ version.slot }} [{{ version.overlay }}]
</li>
{% endfor %}
</ul>
</dd>
{% endif %}
{% if upstream %}
<dt>Upstream versions</dt>
<dd>
<ul>
{% for version in upstream %}
<li>
<img src="{{ STATIC_URL }}img/upstream-icon.png" alt="upstream" title="Upstream" />
{{ version.version }} - {{ version.urls }}
</li>
{% endfor %}
</ul>
</dd>
{% endif %}
<dt>Version history</dt>
<dd>
<ul class="log">
{% for version in vlog %}
{% if version.action == version.VERSION_ADDED %}
<li class="added">
{% else %}
<li class="removed">
{% endif %}
{% if version.overlay == "gentoo" %}
<img src="{{ STATIC_URL }}img/gentoo-icon.png" alt="gentoo" title="In Gentoo" />
{% elif version.overlay %}
<img src="{{ STATIC_URL }}img/overlay-icon.png" alt="overlays" title="In Overlays" />
{% else %}
<img src="{{ STATIC_URL }}img/upstream-icon.png" alt="upstream" title="Upstream" />
{% endif %}
{{ version }} - {{ version.datetime }}
</li>
{% endfor %}
</ul>
</dt>
{% if log %}
<dt>euscan log</dt>
<dd>
<p>Date: {{ log.datetime }}
<pre class="log">
{{ log.result }}
</pre>
</dd>
{% endif %}
</dl>
{% endblock %}

View File

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

View File

@ -0,0 +1,28 @@
{% extends "_base.html" %}
{% block title %}
{{ block.super }} - World scan
{% endblock %}
{% block content %}
<h2>Scan your packages</h2>
<hr />
<p>Import your /var/lib/portage/world file</p>
<form action="{% url djeuscan.views.world_scan %}" method="post" enctype="multipart/form-data">{% csrf_token %}
{{ world_form.as_p }}
<input type="submit" value="Submit" />
</form>
<hr />
<p>Or just list some packages</p>
<form action="{% url djeuscan.views.world_scan %}" method="post">{% csrf_token %}
{{ packages_form.as_p }}
<input type="submit" value="Submit" />
</form>
{% endblock %}

View File

@ -0,0 +1,12 @@
{% extends "euscan/_datatable.html" %}
{% load packages %}
{% block title %}
{{ block.super }} - World Scan
{% endblock %}
{% block content %}
<h2>World scan:</h2>
{% packages packages %}
{% endblock %}

View File

@ -0,0 +1,2 @@
from .models import *
from .views import *

View File

@ -0,0 +1,38 @@
import factory
from djeuscan.models import Herd, Maintainer, Package, Version
class HerdFactory(factory.Factory):
FACTORY_FOR = Herd
herd = 'Test Herd'
email = 'herd@testherd.com'
class MaintainerFactory(factory.Factory):
FACTORY_FOR = Maintainer
herd = 'Test Maintainer'
email = 'maintainer@testmaintainer.com'
class PackageFactory(factory.Factory):
FACTORY_FOR = Package
category = "Test Category"
name = "Test Package"
description = "This is a test package"
homepage = "http://testpackage.com"
class VersionFactory(factory.Factory):
FACTORY_FOR = Version
package = factory.LazyAttribute(lambda a: PackageFactory())
slot = "1"
revision = "1"
version = "0.1"
packaged = True
overlay = "gentoo"
urls = "http://packageurl.com"
alive = True

View File

@ -0,0 +1,22 @@
"""
tests for models
"""
from django.utils import unittest
from django.db import IntegrityError
from djeuscan.tests.euscan_factory import VersionFactory, PackageFactory
class VersionModelTests(unittest.TestCase):
def test_creation(self):
package = PackageFactory.build()
version = VersionFactory.build(package=package)
self.assertEqual(version.package, package)
def test_not_allowed_creation(self):
package = PackageFactory.create()
VersionFactory.create(package=package)
with self.assertRaises(IntegrityError):
VersionFactory.create(package=package)

View File

@ -0,0 +1,12 @@
"""
tests for models
"""
from django.utils import unittest
from django.test.client import RequestFactory
from djeuscan.views import index
class ViewsTests(unittest.TestCase):
pass

View File

@ -43,7 +43,7 @@ urlpatterns = patterns('djeuscan.views',
# Global stuff
(r'^api/', include('djeuscan.api.urls')),
(r'^$', 'index'),
url(r'^$', 'index', name="index"),
url(r'^feed/$', GlobalFeed(), name='global_feed'),
(r'^about/$', 'about'),
(r'^about/api$', 'api'),

View File

@ -1,42 +1,37 @@
""" Views """
from annoying.decorators import render_to
from django.http import Http404
from django.shortcuts import get_object_or_404
from django.db.models import Sum, Max
from django.db.models import Max
from models import Version, Package, Herd, Maintainer, EuscanResult, VersionLog
from forms import WorldForm, PackagesForm
from djeuscan.models import Version, Package, Herd, Maintainer, EuscanResult, \
VersionLog
from djeuscan.forms import WorldForm, PackagesForm
import charts
""" Views """
@render_to('euscan/index.html')
def index(request):
ctx = {}
ctx['n_packaged'] = charts.xint(
Package.objects.aggregate(Sum('n_packaged'))['n_packaged__sum']
)
ctx['n_overlay'] = charts.xint(
Package.objects.aggregate(Sum('n_overlay'))['n_overlay__sum']
)
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_overlay']
ctx['n_packages'] = Package.objects.count()
ctx['n_herds'] = Herd.objects.count()
ctx['n_maintainers'] = Maintainer.objects.count()
context = {
'n_packaged': Package.objects.n_packaged(),
'n_overlay': Package.objects.n_overlay(),
'n_versions': Package.objects.n_versions(),
'n_upstream': Package.objects.n_upstream(),
'n_packages': Package.objects.count(),
'n_herds': Herd.objects.count(),
'n_maintainers': Maintainer.objects.count(),
}
try:
ctx['last_scan'] = EuscanResult.objects.get(
context['last_scan'] = \
EuscanResult.objects.get(
id=EuscanResult.objects.aggregate(Max('id'))['id__max']
).datetime
except EuscanResult.DoesNotExist:
ctx['last_scan'] = None
context['last_scan'] = None
return ctx
return context
@render_to('euscan/logs.html')
@ -46,13 +41,7 @@ def logs(request):
@render_to('euscan/categories.html')
def categories(request):
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': Package.objects.categories()}
@render_to('euscan/category.html')
@ -61,7 +50,7 @@ def category(request, category):
packages = packages.select_related(
'last_version_gentoo', 'last_version_overlay', 'last_version_upstream'
)
print dir(packages[0])
if not packages:
raise Http404
return {'category': category, 'packages': packages}
@ -69,13 +58,7 @@ def category(request, category):
@render_to('euscan/herds.html')
def herds(request):
# FIXME: optimize the query, it uses 'LEFT OUTER JOIN' instead of
# 'INNER JOIN'
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'))
herds = Package.objects.herds()
return {'herds': herds}
@ -91,16 +74,7 @@ def herd(request, herd):
@render_to('euscan/maintainers.html')
def maintainers(request):
maintainers = Package.objects.filter(maintainers__isnull=False)
maintainers = maintainers.values(
'maintainers__id', 'maintainers__name', 'maintainers__email'
)
maintainers = maintainers.annotate(
n_packaged=Sum('n_packaged'),
n_overlay=Sum('n_overlay'),
n_versions=Sum('n_versions')
)
maintainers = Package.objects.maintainers()
return {'maintainers': maintainers}
@ -116,9 +90,7 @@ def maintainer(request, maintainer_id):
@render_to('euscan/overlays.html')
def overlays(request):
overlays = Package.objects.values('version__overlay')
overlays = overlays.exclude(version__overlay='')
overlays = overlays.distinct()
overlays = Package.objects.overlays()
return {'overlays': overlays}