[Pkg-debile-commits] [debile-web] 01/01: WIP: Update debile-web to current db schema and terminology.

Jon Severinsson jonno-guest at moszumanska.debian.org
Thu Mar 13 17:55:58 UTC 2014


This is an automated email from the git hooks/post-receive script.

jonno-guest pushed a commit to branch master
in repository debile-web.

commit c7c3ee6347b9626f3a7bf17aca8d02e86b804330
Author: Jon Severinsson <jon at severinsson.net>
Date:   Tue Mar 11 20:52:04 2014 +0100

    WIP: Update debile-web to current db schema and terminology.
---
 debileweb/blueprints/consts.py             |    2 +
 debileweb/blueprints/forms.py              |    2 +-
 debileweb/blueprints/frontend.py           |  843 +++++++++++---------
 static/css/jquery-ui.css                   | 1175 ----------------------------
 templates/builder.html                     |   36 +
 templates/builder_list_fragment.html       |   37 +
 templates/group.html                       |   57 +-
 templates/group_list_fragment.html         |   28 +
 templates/index.html                       |   38 +-
 templates/job.html                         |  103 +++
 templates/job_list_fragment.html           |   60 +-
 templates/jobs.html                        |   14 +
 templates/machine.html                     |   11 -
 templates/machine_list_fragment.html       |   21 -
 templates/macros.inc.html                  |   10 -
 templates/prefix.html                      |    8 -
 templates/prefix_list.html                 |   37 -
 templates/report.html                      |   35 -
 templates/report_desc_binary.html          |   44 --
 templates/report_desc_source.html          |   36 -
 templates/report_list_binary_fragment.html |   37 -
 templates/report_list_fragment.html        |   52 --
 templates/repository.html                  |   21 -
 templates/search.html                      |   14 +-
 templates/source-not-found.html            |    6 +-
 templates/source.html                      |   78 +-
 templates/source_list.html                 |    8 -
 templates/source_list_fragment.html        |   50 +-
 templates/sources.html                     |   14 +
 templates/user.html                        |   42 +
 templates/worker.html                      |   19 -
 31 files changed, 927 insertions(+), 2011 deletions(-)

diff --git a/debileweb/blueprints/consts.py b/debileweb/blueprints/consts.py
index cdd3164..c6e436b 100644
--- a/debileweb/blueprints/consts.py
+++ b/debileweb/blueprints/consts.py
@@ -28,3 +28,5 @@ PREFIXES = [
     'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
 ]
 
+ENTRIES_PER_PAGE = 20
+ENTRIES_PER_LIST_PAGE = 100
diff --git a/debileweb/blueprints/forms.py b/debileweb/blueprints/forms.py
index 68cbf8c..6bc1c89 100644
--- a/debileweb/blueprints/forms.py
+++ b/debileweb/blueprints/forms.py
@@ -23,5 +23,5 @@ from wtforms.validators import Required
 
 
 class SearchPackageForm(Form):
-    package = TextField('package', validators=[Required()])
+    source = TextField('source', validators=[Required()])
     maintainer = TextField('maintainer', validators=[Required()])
diff --git a/debileweb/blueprints/frontend.py b/debileweb/blueprints/frontend.py
index 48e7153..9caac9d 100644
--- a/debileweb/blueprints/frontend.py
+++ b/debileweb/blueprints/frontend.py
@@ -1,6 +1,7 @@
 # Copyright (c) 2012 Paul Tagliamonte <paultag at debian.org>
 # Copyright (c) 2013 Leo Cavaille <leo at cavaille.net>
 # Copyright (c) 2013 Sylvestre Ledru <sylvestre at debian.org>
+# Copyright (c) 2014 Jon Severinsson <jon at severinsson.net>
 #
 # Permission is hereby granted, free of charge, to any person obtaining a
 # copy of this software and associated documentation files (the "Software"),
@@ -20,28 +21,21 @@
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
 
-from flask import Blueprint, render_template, send_file, request, redirect
+from flask import Blueprint, render_template, request, redirect
 from flask.ext.jsonpify import jsonify
-
-from sqlalchemy import func
 from sqlalchemy.orm import joinedload
-from sqlalchemy.sql.expression import bindparam
 
+from debile.master.utils import make_session
+from debile.master.orm import (Person, Builder, Suite, Component, Arch, Check,
+                               Group, GroupSuite, Source, Maintainer, Binary,
+                               Job, JobDependencies, Result)
 
-from debilemaster.orm import Source, Binary, Machine, User, Job, Group
-from debilemaster.config import Config
-from debilemaster.server import Session
-from debilemaster.archive import UserRepository
+from debileweb.blueprints.forms import SearchPackageForm
+from debileweb.blueprints.consts import PREFIXES, ENTRIES_PER_PAGE, ENTRIES_PER_LIST_PAGE
 
+from datetime import datetime
 from humanize import naturaltime
-from humanize.time import naturaldelta
-
-from datetime import timedelta
-import datetime as dt
-import os.path
-import re
-from forms import SearchPackageForm
-from consts import PREFIXES_DEFAULT
+import os
 
 frontend = Blueprint('frontend', __name__, template_folder='templates')
 
@@ -50,442 +44,549 @@ frontend = Blueprint('frontend', __name__, template_folder='templates')
 def ago_display(when):
     if when is None:
         return "never"
-    td = dt.datetime.utcnow() - when
+    td = datetime.utcnow() - when
     return naturaltime(td)
 
 
-def get_packages_prefixes():
-    """
-    returns the packages prefixes (a, b, ..., liba, libb, ..., y, z)
-    Note that this could be computed from the database ... but since we
-    are rebuilding Debian, we will have all letters + lib*
-    """
-    return PREFIXES_DEFAULT
-
-def get_package_link(p):
-    if p.type == "source":
-        return "/source/%s/%s/%s/%s" % (p.user.login, p.name, p.version, p.run)
-    else:
-        return "/notimplementedyet"
-
-
-def get_machine_link(m):
-    return "/machine/%s" % m.name
-
-
 @frontend.route("/")
 def index():
-    session = Session()
-    active_jobs = session.query(Job)\
-        .options(joinedload('machine'))\
-        .filter(Job.machine != None)\
-        .filter(Job.finished_at == None)\
+    session = make_session()
+
+    groups = session.query(Group)\
+        .order_by(Group.name.asc())\
         .all()
-    machines = session.query(Machine).options(joinedload('jobs')).all()
-    active_jobs_info = []
-    for j in active_jobs:
+    builders = session.query(Builder)\
+        .order_by(Builder.name.asc())\
+        .all()
+
+    groups_info = []
+    for group in groups:
         info = {}
-        info['job'] = j
-        info['package_link'] = get_package_link(j.package)
-        if j.machine:
-            info['machine_link'] = get_machine_link(j.machine)
-        active_jobs_info.append(info)
+        info['group'] = group
+        info['group_link'] = "/group/%s" % group.name
+        info['maintainer_link'] = "/user/%s" % group.maintainer.email
+        groups_info.append(info)
+
+    builders_info = []
+    for builder in builders:
+        info = {}
+        info['builder'] = builder
+        info['builder_link'] = "/builder/%s" % builder.name
+        info['maintainer_link'] = "/user/%s" % group.maintainer.email
+        jobs = session.query(Job).join(Source)\
+            .filter(Job.assigned_at != None)\
+            .filter(Job.finished_at == None)\
+            .filter(Job.builder == builder)\
+            .order_by(Job.id.desc())\
+            .all()
+        jobs_info = []
+        for job in jobs:
+            jobinfo = {}
+            jobinfo['job'] = job
+            jobinfo['job_link'] = "/job/%s/%s/%s/%s" % \
+                (job.group.name, job.source.name, job.source.version, job.id)
+            jobinfo['source_link'] = "/source/%s/%s/%s" % \
+                (job.group.name, job.source.name, job.source.version)
+            jobs_info.append(jobinfo)
+        info['jobs_info'] = jobs_info
+        builders_info.append(info)
 
     pending_jobs = session.query(Job)\
         .filter(Job.assigned_at == None)\
         .count()
-
     form = SearchPackageForm()
-    packages_prefixes = get_packages_prefixes()
 
     return render_template('index.html', **{
-        "active_jobs_info": active_jobs_info,
+        "groups_info": groups_info,
+        "builders_info": builders_info,
         "pending_jobs": pending_jobs,
-	"packages_prefixes": packages_prefixes,
+        "prefixes": PREFIXES,
         "form": form
     })
 
+
+ at frontend.route("/maintainer/<search>/", methods=['POST', 'GET'])
+ at frontend.route("/maintainer/<search>/<page>")
+ at frontend.route("/source/<search>/", methods=['POST', 'GET'])
+ at frontend.route("/source/<search>/<page>")
 @frontend.route("/sources/")
-def source_list():
-    session = Session()
-    count = 100
-    sources = session.query(Source)\
-        .options(joinedload(Source.user))\
-        .options(joinedload(Source.group))\
-        .order_by(Source.created_at.desc())\
-        .limit(count)
+ at frontend.route("/sources/<prefix>/")
+ at frontend.route("/sources/<prefix>/<page>/")
+def sources(search="", prefix="recent", page=0):
+    page = int(page)
+    session = make_session()
+
+    if request.path == "/maintainer/search/":
+        return redirect('/maintainer/' + request.form['maintainer'] + '/')
+    if request.path == "/source/search/":
+        return redirect('/source/' + request.form['source'] + '/')
+
+    if request.path.startswith("/maintainer/"):
+        source_count = session.query(Source)\
+            .count()
+        desc = "Search results for maintainer '%s'" % search
+        source_count = session.query(Source)\
+            .filter(Source.maintainers.any(Maintainer.name.contains(search) |
+                                           Maintainer.email.contains(search)))\
+            .count()
+        sources = session.query(Source)\
+            .filter(Source.maintainers.any(Maintainer.name.contains(search) |
+                                           Maintainer.email.contains(search)))\
+            .order_by(Source.name.asc(), Source.id.desc())\
+            .offset(page * ENTRIES_PER_LIST_PAGE)\
+            .limit(ENTRIES_PER_LIST_PAGE)\
+            .all()
+    elif request.path.startswith("/source/"):
+        desc = "Search results for source '%s'" % search
+        source_count = session.query(Source)\
+            .filter(Source.name.contains(search))\
+            .count()
+        sources = session.query(Source)\
+            .filter(Source.name.contains(search))\
+            .order_by(Source.name.asc(), Source.id.desc())\
+            .offset(page * ENTRIES_PER_LIST_PAGE)\
+            .limit(ENTRIES_PER_LIST_PAGE)\
+            .all()
+    elif prefix == "recent":
+        desc = "All recent sources."
+        source_count = session.query(Source).count()
+        sources = session.query(Source)\
+            .order_by(Source.id.desc())\
+            .offset(page * ENTRIES_PER_LIST_PAGE)\
+            .limit(ENTRIES_PER_LIST_PAGE)\
+            .all()
+    elif prefix == "incomplete":
+        desc = "All incomplete sources."
+        source_count = session.query(Source)\
+            .filter(Source.jobs.any(Job.finished_at == None))\
+            .count()
+        sources = session.query(Source)\
+            .filter(Source.jobs.any(Job.finished_at == None))\
+            .order_by(Source.name.asc(), Source.id.desc())\
+            .offset(page * ENTRIES_PER_LIST_PAGE)\
+            .limit(ENTRIES_PER_LIST_PAGE)\
+            .all()
+    elif prefix == "l":
+        desc = "All sources for packages beginning with 'l'"
+        source_count = session.query(Source)\
+            .filter(Source.name.startswith("l"))\
+            .filter(~Source.name.startswith("lib"))\
+            .count()
+        sources = session.query(Source)\
+            .filter(Source.name.startswith("l"))\
+            .filter(~Source.name.startswith("lib"))\
+            .order_by(Source.name.asc(), Source.id.desc())\
+            .offset(page * ENTRIES_PER_LIST_PAGE)\
+            .limit(ENTRIES_PER_LIST_PAGE)\
+            .all()
+    else:
+        desc = "All sources for packages beginning with '%s'" % prefix
+        source_count = session.query(Source)\
+            .filter(Source.name.startswith(prefix))\
+            .count()
+        sources = session.query(Source)\
+            .filter(Source.name.startswith(prefix))\
+            .order_by(Source.name.asc(), Source.id.desc())\
+            .offset(page * ENTRIES_PER_LIST_PAGE)\
+            .limit(ENTRIES_PER_LIST_PAGE)\
+            .all()
+
     sources_info = []
-    for s in sources:
+    for source in sources:
         info = {}
-        info['source'] = s
-        info['source_link'] = "/source/%s/%s/%s/%s" % (s.user.login, s.name, s.version, s.run)
-        info['group_link'] = "/group/%s" % s.group.name
-        info['user_link'] = "/worker/%s" % s.user.login
-        info['user_repository_link'] = "/repository/%s" % s.user.login
+        info['source'] = source
+        info['source_link'] = "/source/%s/%s/%s" % \
+            (source.group.name, source.name, source.version)
+        info['group_link'] = "/group/%s" % source.group.name
+        info['uploader_link'] = "/user/%s" % source.uploader.email
         sources_info.append(info)
 
-    return render_template('source_list.html', **{
+    info = {}
+    info['desc'] = desc
+    info['prev_link'] = "/sources/%s/%d" % (prefix, page-1) \
+        if page > 0 else None
+    info['next_link'] = "/sources/%s/%d" % (prefix, page+1) \
+        if source_count > (page+1) * ENTRIES_PER_LIST_PAGE else None
+
+    return render_template('sources.html', **{
+        "info": info,
         "sources_info": sources_info,
-        "count": count,
     })
 
 
+ at frontend.route("/jobs/")
+ at frontend.route("/jobs/<prefix>/")
+ at frontend.route("/jobs/<prefix>/<page>/")
+def jobs(prefix="recent", page=0):
+    page = int(page)
+    session = make_session()
+
+    if prefix == "recent":
+        desc = "All recent jobs."
+        job_count = session.query(Job).count()
+        jobs = session.query(Job).join(Source).join(Check)\
+            .order_by(Source.id.desc())\
+            .offset(page * ENTRIES_PER_LIST_PAGE)\
+            .limit(ENTRIES_PER_LIST_PAGE)\
+            .all()
+    elif prefix == "incomplete":
+        desc = "All incomplete jobs."
+        job_count = session.query(Job)\
+            .filter(Job.finished_at == None)\
+            .count()
+        jobs = session.query(Job).join(Source).join(Check)\
+            .filter(Job.finished_at == None)\
+            .order_by(Source.name.asc(), Source.id.desc(),
+                      Check.build.desc(), Check.id.asc(),
+                      Job.id.asc())\
+            .offset(page * ENTRIES_PER_LIST_PAGE)\
+            .limit(ENTRIES_PER_LIST_PAGE)\
+            .all()
+    elif prefix == "l":
+        desc = "All jobs for packages beginning with 'l'"
+        job_count = session.query(Job).join(Source)\
+            .filter(Source.name.startswith("l"))\
+            .filter(~Source.name.startswith("lib"))\
+            .count()
+        jobs = session.query(Job).join(Source).join(Check)\
+            .filter(Source.name.startswith("l"))\
+            .filter(~Source.name.startswith("lib"))\
+            .order_by(Source.name.asc(), Source.id.desc(),
+                      Check.build.desc(), Check.id.asc(),
+                      Job.id.asc())\
+            .offset(page * ENTRIES_PER_LIST_PAGE)\
+            .limit(ENTRIES_PER_LIST_PAGE)\
+            .all()
+    else:
+        desc = "All jobs for packages beginning with '%s'" % prefix
+        job_count = session.query(Job).join(Source)\
+            .filter(Source.name.startswith(prefix))\
+            .count()
+        jobs = session.query(Job).join(Source).join(Check)\
+            .filter(Source.name.startswith(prefix))\
+            .order_by(Source.name.asc(), Source.id.desc(),
+                      Check.build.desc(), Check.id.asc(),
+                      Job.id.asc())\
+            .offset(page * ENTRIES_PER_LIST_PAGE)\
+            .limit(ENTRIES_PER_LIST_PAGE)\
+            .all()
+
+    jobs_info = []
+    for job in jobs:
+        info = {}
+        info['job'] = job
+        info['job_link'] = "/job/%s/%s/%s/%s" % \
+            (job.group.name, job.source.name, job.source.version, job.id)
+        info['source_link'] = "/source/%s/%s/%s" % \
+            (job.group.name, job.source.name, job.source.version)
+        info['group_link'] = "/group/%s" % job.group.name
+        info['builder_link'] = "/builder/%s" % job.builder.name \
+            if job.builder else None
+        jobs_info.append(info)
+
+    info = {}
+    info['desc'] = desc
+    info['prev_link'] = "/jobs/%s/%d" % (prefix, page-1) \
+        if page > 0 else None
+    info['next_link'] = "/jobs/%s/%d" % (prefix, page+1) \
+        if job_count > (page+1) * ENTRIES_PER_LIST_PAGE else None
+
+    return render_template('jobs.html', **{
+        "info": info,
+        "jobs_info": jobs_info,
+    })
 
- at frontend.route("/maintainer/<nameItem>/", methods=['POST','GET'])
- at frontend.route("/prefix/<nameItem>/")
-def list_packages(nameItem=0):
-    if request.method == 'POST':
-        # Switch a better url
-        return redirect('/maintainer/' + re.search(r".*<(.*)>",request.form['maintainer']).group(1) + '/')
 
-    session = Session()
-    if request.path.startswith("/maintainer/"):
-        # Maintainer
-        sources = session.query(Source)\
-            .filter(Source.maintainer.contains(nameItem))\
-            .distinct(Source.name)\
-            .order_by(Source.name.desc(), Source.version.desc())
-    else:
-        sources = session.query(Source)\
-            .filter(Source.name.startswith(nameItem))\
-            .distinct(Source.name)\
-            .order_by(Source.name.desc(), Source.version.desc())
+ at frontend.route("/group/<name>/")
+ at frontend.route("/group/<name>/<page>/")
+def group(name, page=0):
+    page = int(page)
+    session = make_session()
+
+    group = session.query(Group)\
+        .filter(Group.name == name)\
+        .one()
+
+    source_count = session.query(Source)\
+        .filter(Source.group == group)\
+        .count()
+    sources = session.query(Source)\
+        .filter(Source.group == group)\
+        .order_by(Source.id.desc())\
+        .offset(page * ENTRIES_PER_PAGE)\
+        .limit(ENTRIES_PER_PAGE)\
+        .all()
 
     sources_info = []
-    for s in sources:
+    for source in sources:
         info = {}
-        info['source'] = s
-        info['source_link'] = "/source/%s/%s/%s/%s" % (s.user.login, s.name, "latest", s.run)
+        info['source'] = source
+        info['source_link'] = "/source/%s/%s/%s" % \
+            (source.group.name, source.name, source.version)
+        info['uploader_link'] = "/user/%s" % source.uploader.email
         sources_info.append(info)
 
-    return render_template('prefix.html', **{
-        "sources": sources_info,
-        "prefix": nameItem,
+    info = {}
+    info['maintainer_link'] = "/user/%s" % group.maintainer.email
+    info['prev_link'] = "/group/%s/%d" % (group.name, page-1) \
+        if page > 0 else None
+    info['next_link'] = "/group/%s/%d" % (group.name, page+1) \
+        if source_count > (page+1) * ENTRIES_PER_PAGE else None
+
+    return render_template('group.html', **{
+        "group": group,
+        "info": info,
+        "sources_info": sources_info,
     })
 
- at frontend.route("/group/<group_id>/")
- at frontend.route("/group/<group_id>/<page>/")
-def group_list(group_id, page=0):
+
+ at frontend.route("/builder/<name>")
+ at frontend.route("/builder/<name>/<page>")
+def builder(name, page=0):
     page = int(page)
-    # FIXME : unsafe code, catch exceptions
-    session = Session()
-    g = session.query(Group).filter(Group.name == group_id).one()
-    sources = session.query(Source).filter(Source.group == g).order_by(Source.created_at.asc()).paginate(page, per_page=15)
+    session = make_session()
+
+    builder = session.query(Builder)\
+        .filter(Builder.name == name)\
+        .one()
+
+    job_count = session.query(Job).filter(Job.builder == builder).count()
+    jobs = session.query(Job).join(Source)\
+        .filter(Job.builder == builder)\
+        .order_by(Job.id.desc())\
+        .offset(page * ENTRIES_PER_PAGE)\
+        .limit(ENTRIES_PER_PAGE)\
+        .all()
 
-    return render_template('group.html', **{
-        "sources": sources,
-        "group_id": group_id,
-        "page": page,
+    jobs_info = []
+    for job in jobs:
+        info = {}
+        info['job'] = job
+        info['job_link'] = "/job/%s/%s/%s/%s" % \
+            (job.group.name, job.source.name, job.source.version, job.id)
+        info['source_link'] = "/source/%s/%s/%s" % \
+            (job.group.name, job.source.name, job.source.version)
+        info['group_link'] = "/group/%s" % job.group.name
+        jobs_info.append(info)
+
+    info = {}
+    info['maintainer_link'] = "/user/%s" % builder.maintainer.email
+    info['prev_link'] = "/builder/%s/%d" % (builder.name, page-1) \
+        if page > 0 else None
+    info['next_link'] = "/builder/%s/%d" % (builder.name, page+1) \
+        if job_count > (page+1) * ENTRIES_PER_PAGE else None
+
+    return render_template('machine.html', **{
+        "builder": builder,
+        "jobs_info": jobs_info,
+        "info": info,
     })
 
 
- at frontend.route("/source/search/", methods=['POST'])
- at frontend.route("/source/<owner_name>/<package_name>/<package_version>/<int:run_number>")
-def source(package_name="", owner_name="fred", package_version="latest", run_number=1):
-    if request.method == 'POST':
-        # Switch a better url
-        return redirect('/source/' + owner_name + '/' + request.form['package'] + '/' + package_version + '/' + str(run_number))
+ at frontend.route("/user/<email>/")
+ at frontend.route("/user/<email>/<page>/")
+def user(email, page=0):
+    page = int(page)
+    session = make_session()
 
-    session = Session()
+    user = session.query(Person)\
+        .filter(Person.email == email)\
+        .one()
 
-    # Let's compute all the versions that exists for this package
-    versions_query = session.query(Source.version)\
-        .join(Source.user)\
-        .filter(Source.name == package_name)\
-        .filter(User.login == owner_name)
-    versions = sorted(set([e[0] for e in versions_query.all()]))
+    groups = session.query(Group)\
+        .filter(Group.maintainer == user)\
+        .order_by(Group.name.asc())\
+        .all()
 
-    if len(versions) == 0:
-        return render_template('source-not-found.html', **{
-                "package_name": package_name
-                })
+    builders = session.query(Builder)\
+        .filter(Builder.maintainer == user)\
+        .order_by(Builder.name.asc())\
+        .all()
 
-    latest_version = versions[-1]
-    if package_version == 'latest':
-        this_version = latest_version
-    else:
-        this_version = package_version
+    source_count = session.query(Source)\
+        .filter(Source.uploader == user)\
+        .count()
+    sources = session.query(Source)\
+        .filter(Source.uploader == user)\
+        .order_by(Source.id.desc())\
+        .offset(page * ENTRIES_PER_PAGE)\
+        .limit(ENTRIES_PER_PAGE)\
+        .all()
+
+    groups_info = []
+    for group in groups:
+        info = {}
+        info['group'] = group
+        info['group_link'] = "/group/%s" % group.name
+        groups_info.append(info)
+
+    builders_info = []
+    for builder in builders:
+        info = {}
+        info['builder'] = builders
+        info['builder_link'] = "/builder/%s" % builder.name
+        jobs = session.query(Job).join(Source)\
+            .filter(Job.assigned_at != None)\
+            .filter(Job.finished_at == None)\
+            .filter(Job.builder == builder)\
+            .order_by(Job.id.desc())\
+            .all()
+        jobs_info = []
+        for job in jobs:
+            jobinfo = {}
+            jobinfo['job'] = job
+            jobinfo['job_link'] = "/job/%s/%s/%s/%s" % \
+                (job.group.name, job.source.name, job.source.version, job.id)
+            jobinfo['source_link'] = "/source/%s/%s/%s" % \
+                (job.group.name, job.source.name, job.source.version)
+            jobs_info.append(jobinfo)
+        info['jobs_info'] = jobs_info
+        builders_info.append(info)
+
+    sources_info = []
+    for source in sources:
+        info = {}
+        info['source'] = source
+        info['source_link'] = "/source/%s/%s/%s" % \
+            (source.group.name, source.name, source.version)
+        info['group_link'] = "/group/%s" % source.group.name
+        sources_info.append(info)
 
-    # All runs that exist for this version
-    runs_query = session.query(Source.run)\
-        .join(Source.user)\
+    info = {}
+    info['prev_link'] = "/user/%s/%d" % (user.email, page-1) \
+        if page > 0 else None
+    info['next_link'] = "/user/%s/%d" % (user.email, page+1) \
+        if source_count > (page+1) * ENTRIES_PER_PAGE else None
+
+    return render_template('user.html', **{
+        "user": user,
+        "info": info,
+        "groups_info": groups,
+        "builders_info": builders,
+        "sources_info": sources,
+    })
+
+
+ at frontend.route("/source/<group_name>/<package_name>/<suite_or_version>/")
+def source(group_name, package_name, suite_or_version):
+    session = make_session()
+
+    source = session.query(Source.version)\
+        .filter(Group.name == group_name)\
         .filter(Source.name == package_name)\
-        .filter(User.login == owner_name)\
-        .filter(Source.version == this_version)\
-        .order_by(Source.run.asc())
-    runs = [e[0] for e in runs_query.all()]
+        .filter(Source.version == suite_or_version |
+                Suite.name == suite_or_version)\
+        .order_by(Source.id.desc()).first()
 
-    if len(runs) == 0:
+    if not source:
         return render_template('source-not-found.html', **{
-                "package_name": package_name,
-                "version": this_version
-                })
-
-    
-    latest_run = runs[-1]
-    if run_number == '0':
-        this_run = latest_run
-    else:
-        this_run = run_number
-
-    # Join load the user to show details about the source owner
-    package_query = session.query(Source)\
-        .options(joinedload('user'))\
-        .options(joinedload('jobs'))\
-        .options(joinedload('binaries'))\
+            "group_name": group_name,
+            "package_name": package_name,
+            "suite_or_version": suite_or_version,
+        })
+
+    # Find all versions of this package
+    versions = session.query(Source.version)\
+        .filter(Group.name == group_name)\
         .filter(Source.name == package_name)\
-        .filter(User.login == owner_name)\
-        .filter(Source.version == this_version)\
-        .filter(Source.run == this_run)
-
-    try:
-        package = package_query.one()
-    except:
-        raise Exception("This resource does not exist")
-
-    # Compute description section
-    desc = {}
-    desc['user_link'] = "/worker/%s" % package.user.login
-    desc['user_repository_link'] = "/repository/%s" % package.user.login
-
-    desc['run'] = run_number
-    # desc['pool_link'] =
-
-    # Fill in the run sections if need be
-    # Remember that multiple runs cannot exist with fred auto-build
-    if len(runs) > 1:
-        multiple_runs = True
-    else:
-        multiple_runs = False
-    runs_info = []
-    if multiple_runs:
-        for r in runs:
-            href = "/source/%s/%s/%s/%s" % (owner_name,
-                                            package_name,
-                                            package_version,
-                                            r
-                                            )
-            runs_info.append((r, href))
-
-    # Fill in the version sections
-    if len(versions) > 1:
-        multiple_versions = True
-    else:
-        multiple_versions = False
+        .order_by(Source.id.desc()).all()
+
     versions_info = []
-    if multiple_versions:
-        for v in versions:
-            href = "/source/%s/%s/%s/%s" % (owner_name, package_name, v, 1)
-            versions_info.append((v, href))
-
-    # Compute infos to display the job parts
-    # Iterate through all jobs
-    # Job total counters + compute some links
-    source_jobs = session.query(Job)\
-        .options(joinedload('machine'))\
-        .filter(Job.package == package)\
-        .order_by(Job.type, Job.subtype)\
+    if len(versions) > 1:
+        for version in versions:
+            href = "/source/%s/%s/%s" % \
+                (group_name, package_name, version)
+            versions_info.append((version, href))
+
+    jobs = session.query(Job).join(Check)\
+        .filter(Job.source == source)\
+        .order_by(Check.build.desc(), Check.id.asc(), Job.id.asc())\
         .all()
 
-    total = len(source_jobs)
+    total = len(jobs)
     unfinished = 0
-    source_jobs_info = []
-    for j in source_jobs:
+    jobs_info = []
+    for job in jobs:
         info = {}
-        info['job'] = j
-        info['job_link'] = '/report/%s/%s/%s/%s#full_log' % (package_name, this_version, j.type, j.id)
-        if j.type == "clanganalyzer":
-            # Special case (I know) for clang to point directly to the HTML report
-            # TODO: update the path to a nicer one (like job_link)
-            info['job_report_link'] = '/static-job-reports/%s/scan-build/' % j.id
-        else:
-            info['job_report_link'] = info['job_link']
-        if j.machine:
-            info['job_machine_link'] = '/machine/%s' % j.machine.name
-        if not j.is_finished():
+        info['job'] = job
+        info['job_link'] = '/job/%s/%s/%s/%d' % \
+            (group_name, package_name, source.version, job.id)
+        info['builder_link'] = '/builder/%s' % job.builder.name \
+            if job.builder else None
+        if job.finished_at is None:
             unfinished += 1
-            if j.machine is None:
-                info['status'] = 'pending'
-            else:
-                info['status'] = 'running'
+            info['status'] = 'running' if job.assigned_at else 'pending'
         else:
             info['status'] = 'finished'
 
-        source_jobs_info.append(info)
-
-    binaries_jobs = session.query(Job)\
-        .options(joinedload('machine'))\
-        .join(Binary, Job.package_id == Binary.package_id)\
-        .filter(Binary.source_id == package.source_id)\
-        .order_by(Job.type, Job.subtype, Binary.name)\
-        .all()
+        jobs_info.append(info)
 
-    binaries_jobs_info = []
-    for j in binaries_jobs:
-        info = {}
-        info['job'] = j
-        info['job_link'] = '/report/%s/%s/%s/%s#full_log' % (package_name, this_version, j.type, j.id)
-        if j.machine:
-            info['job_machine_link'] = '/machine/%s' % j.machine.name
-        if not j.is_finished():
-            unfinished += 1
-            if j.machine:
-                info['status'] = 'running'
-            else:
-                info['status'] = 'pending'
-        else:
-            info['status'] = 'finished'
-        binaries_jobs_info.append(info)
+    info = {}
+    info["job_status"] = (total, unfinished),
+    info['group_link'] = "/group/%s" % source.group.name
+    info['uploader_link'] = "/user/%s" % source.uploader.name
 
     return render_template('source.html', **{
-        "desc": desc,
-        "multiple_runs": multiple_runs,
-        "runs_info": runs_info,
-        "latest_run": latest_run,
-        "multiple_versions": multiple_versions,
-        "latest_version": latest_version,
+        "job": job,
+        "info": info,
         "versions_info": versions_info,
-        "package": package,
-        "package_job_status": (total, unfinished),
-        "source_jobs_info": source_jobs_info,
-        "binaries_jobs_info": binaries_jobs_info
-    })
-
-
- at frontend.route("/machine/<machine_name>")
-def machine(machine_name):
-    session = Session()
-    # FIXME : unsafe code, catch exceptions
-    machine = session.query(Machine).filter(Machine.name == machine_name).one()
-    return render_template('machine.html', **{
-        "machine": machine
+        "jobs_info": jobs_info,
     })
 
 
- at frontend.route("/worker/<worker_login>")
-def worker(worker_login):
-    session = Session()
-    # FIXME : unsafe code, catch exceptions                                                                            
-    user = session.query(User).filter(User.login == worker_login).one()
+ at frontend.route("/job/<job_id>/")
+ at frontend.route("/job/<group_name>/<package_name>/<package_version>/<job_id>/")
+def job(job_id, group_name="", package_name="", version=""):
+    job_id = int(job_id)
+    session = make_session()
 
-    ur = UserRepository(user)
-    dput_upload_profile = ur.generate_dputprofile()
-
-    return render_template('worker.html', **{
-        "worker": user,
-        "dput_upload_profile": dput_upload_profile
-    })
+    job = session.query(Job).get(job_id)
 
- at frontend.route("/repository/<worker_login>")
-def repository(worker_login):
-    session = Session()
-    # FIXME : unsafe code, catch exceptions
-    user = session.query(User).filter(User.login == worker_login).one()
-
-    ur = UserRepository(user)
-    apt_binary_list = ur.generate_aptbinarylist()
-    apt_source_list = ur.generate_aptsourcelist()
-
-    return render_template('repository.html', **{
-        "worker": user,
-        "apt_binary_list": apt_binary_list,
-        "apt_source_list": apt_source_list
-    })
-
-
- at frontend.route("/report/<job_id>/")
- at frontend.route("/report/<package_name>/<version>/<job_type>/<job_id>/")
-def report(job_id, package_name="", version="", job_type=""):
-# TODO : design architecture Pending, firewose ?
-#    report = Report.load(report_id)
-    config = Config()
-    session = Session()
-    job_query = session.query(Job).filter(Job.id == job_id)
-    try:
-        job = job_query.one()
-    except:
-        raise Exception("This resource does not exist")
-
-    job_info = {}
-    job_info['job'] = job
     time_diff = job.finished_at - job.assigned_at
     hours, remainder = divmod(time_diff.total_seconds(), 3600)
     minutes, seconds = divmod(remainder, 60)
-    job_info['job_runtime'] = '%dh %02dm %02ds' % (hours, minutes, seconds)
-    job_info['job_runtime_type'] = type(job.finished_at - job.assigned_at)
-    job_info['machine_link'] = "/machine/%s" % job.machine.name
-    if job.package.type == "source":
-        job_info['package_link'] = '/source/%s/%s/%s/%s' % (job.package.user.login, job.package.name, job.package.version, job.package.run)
-    else:
-        pool = os.path.join(config.get('paths', 'pool_url'), str(job.package.source.package_id), job.package.arch, job.package.deb)
-        job_info['deb_link'] = pool
-        job_info['source_link'] = '/source/%s/%s/%s/%s' % (job.package.source.user.login, job.package.source.name, job.package.source.version, job.package.source.run)
-
-    log_path = os.path.join(config.get('paths', 'jobs_path'),
-                            job_id, 'log.txt')
 
-    firehose_link = "/static-job-reports/%s/firehose.xml" % job_id
-    log_link = "/static-job-reports/%s/log.txt" % job_id
-
-    ### SCANDALOUS HACK
-    if job.type == 'clanganalyzer':
-        scanbuild_link = "/static-job-reports/%s/scan-build/" % job_id
-    else:
-        scanbuild_link = ""
-
-    log = []
-    if os.path.exists(log_path):
-        log = (x.decode('utf-8') for x in open(log_path, 'r'))
-
-    return render_template('report.html', **{
-        "job_info": job_info,
-        "log_link": log_link,
-        "firehose_link": firehose_link,
-        "scanbuild_link": scanbuild_link,
-        "log": log,
+    info = {}
+    info['job_runtime'] = '%dh %02dm %02ds' % \
+        (hours, minutes, seconds)
+    info['source_link'] = '/source/%s/%s/%s' % \
+        (job.group.name, job.source.name, job.source.version)
+    info['binary_link'] = '/job/%s/%s/%s/%d' % \
+        (job.group.name, job.binary.name, job.binary.version, job.binary.build_job_id) if job.binary else None
+    info['builder_link'] = "/builder/%s" % job.builder.name
+
+    info['dud_name'] = "%d.dud" % job.id
+    info['log_name'] = "%d.log" % job.id
+    info['firehose_name'] = "%d.firehose.xml" % job.id
+    special_files = [info['dud_name'], info['log_name'], info['firehose_name']]
+    info['files'] = sorted([x for x in os.listdir(job.files_path) if x not in special_files])
+
+    return render_template('job.html', **{
+        "job": job,
+        "info": info,
     })
 
 
- at frontend.route("/report/firehose/<job_id>/")
-def report_firehose(job_id):
-    config = Config()
-    firehose_path = os.path.join(config.get('paths', 'jobs_path'),
-                                 job_id, 'firehose.xml')
-
-    if os.path.exists(firehose_path):
-        return send_file(firehose_path, mimetype='application/xml',
-                         as_attachment=True, attachment_filename='firehose.xml')
-
-
- at frontend.route("/report/log/<job_id>/")
-def report_log(job_id):
-    config = Config()
-    log_path = os.path.join(config.get('paths', 'jobs_path'),
-                            job_id, 'log')
-
-    if os.path.exists(log_path):
-        return send_file(log_path, mimetype='text/plain', as_attachment=True,
-                         attachment_filename='log.txt')
-
-
- at frontend.route('/_search_package')
-def search_package():
+ at frontend.route('/_search_source')
+def search_source():
     search = request.args.get('search[term]')
-    session = Session()
-    packages_query = session.query(Source.name)\
-        .filter(Source.name.startswith(search)).group_by(Source.name).limit(10)
-    result = [r[0] for r in packages_query]
+    session = make_session()
+    query = session.query(Source.name)\
+        .filter(Source.name.startswith(search))\
+        .group_by(Source.name).limit(10)
+    result = [r[0] for r in query]
     return jsonify(result)
 
+
 @frontend.route('/_search_maintainer')
 def search_maintainer():
     search = request.args.get('search[term]')
-    session = Session()
-    print "foo" + search
-    maintainers_query = session.query(Source.maintainer)\
-        .filter(Source.maintainer.contains(search))\
-        .group_by(Source.maintainer).limit(10)
-    result = [r[0] for r in maintainers_query]
+    session = make_session()
+    query = session.query(Maintainer.name, Maintainer.email)\
+        .filter(Maintainer.name.startswith(search) |
+                Maintainer.email.startswith(search))\
+        .group_by(Maintainer.name, Maintainer.email).limit(10)
+    result = [r[0] if r[0].startswith(search) else r[1] for r in query]
     return jsonify(result)
 
 
diff --git a/static/css/jquery-ui.css b/static/css/jquery-ui.css
deleted file mode 100644
index 94cbfca..0000000
--- a/static/css/jquery-ui.css
+++ /dev/null
@@ -1,1175 +0,0 @@
-/*! jQuery UI - v1.10.2 - 2013-03-14
-* http://jqueryui.com
-* Includes: jquery.ui.core.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css
-* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Lucida%20Grande%2CLucida%20Sans%2CArial%2Csans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=5px&bgColorHeader=5c9ccc&bgTextureHeader=gloss_wave&bgImgOpacityHeader=55&borderColorHeader=4297d7&fcHeader=ffffff&iconColorHeader=d8e7f3&bgColorContent=fcfdfd&bgTextureContent=inset_hard&bgImgOpacityContent=100&borderColorContent=a6c9e2&fcContent=222222&iconColorContent=469bdd&bgColorDefault=dfeffc&bgTextureDef [...]
-* Copyright 2013 jQuery Foundation and other contributors Licensed MIT */
-
-/* Layout helpers
-----------------------------------*/
-.ui-helper-hidden {
-	display: none;
-}
-.ui-helper-hidden-accessible {
-	border: 0;
-	clip: rect(0 0 0 0);
-	height: 1px;
-	margin: -1px;
-	overflow: hidden;
-	padding: 0;
-	position: absolute;
-	width: 1px;
-}
-.ui-helper-reset {
-	margin: 0;
-	padding: 0;
-	border: 0;
-	outline: 0;
-	line-height: 1.3;
-	text-decoration: none;
-	font-size: 100%;
-	list-style: none;
-}
-.ui-helper-clearfix:before,
-.ui-helper-clearfix:after {
-	content: "";
-	display: table;
-	border-collapse: collapse;
-}
-.ui-helper-clearfix:after {
-	clear: both;
-}
-.ui-helper-clearfix {
-	min-height: 0; /* support: IE7 */
-}
-.ui-helper-zfix {
-	width: 100%;
-	height: 100%;
-	top: 0;
-	left: 0;
-	position: absolute;
-	opacity: 0;
-	filter:Alpha(Opacity=0);
-}
-
-.ui-front {
-	z-index: 100;
-}
-
-
-/* Interaction Cues
-----------------------------------*/
-.ui-state-disabled {
-	cursor: default !important;
-}
-
-
-/* Icons
-----------------------------------*/
-
-/* states and images */
-.ui-icon {
-	display: block;
-	text-indent: -99999px;
-	overflow: hidden;
-	background-repeat: no-repeat;
-}
-
-
-/* Misc visuals
-----------------------------------*/
-
-/* Overlays */
-.ui-widget-overlay {
-	position: fixed;
-	top: 0;
-	left: 0;
-	width: 100%;
-	height: 100%;
-}
-.ui-accordion .ui-accordion-header {
-	display: block;
-	cursor: pointer;
-	position: relative;
-	margin-top: 2px;
-	padding: .5em .5em .5em .7em;
-	min-height: 0; /* support: IE7 */
-}
-.ui-accordion .ui-accordion-icons {
-	padding-left: 2.2em;
-}
-.ui-accordion .ui-accordion-noicons {
-	padding-left: .7em;
-}
-.ui-accordion .ui-accordion-icons .ui-accordion-icons {
-	padding-left: 2.2em;
-}
-.ui-accordion .ui-accordion-header .ui-accordion-header-icon {
-	position: absolute;
-	left: .5em;
-	top: 50%;
-	margin-top: -8px;
-}
-.ui-accordion .ui-accordion-content {
-	padding: 1em 2.2em;
-	border-top: 0;
-	overflow: auto;
-}
-.ui-autocomplete {
-	position: absolute;
-	top: 0;
-	left: 0;
-	cursor: default;
-}
-.ui-button {
-	display: inline-block;
-	position: relative;
-	padding: 0;
-	line-height: normal;
-	margin-right: .1em;
-	cursor: pointer;
-	vertical-align: middle;
-	text-align: center;
-	overflow: visible; /* removes extra width in IE */
-}
-.ui-button,
-.ui-button:link,
-.ui-button:visited,
-.ui-button:hover,
-.ui-button:active {
-	text-decoration: none;
-}
-/* to make room for the icon, a width needs to be set here */
-.ui-button-icon-only {
-	width: 2.2em;
-}
-/* button elements seem to need a little more width */
-button.ui-button-icon-only {
-	width: 2.4em;
-}
-.ui-button-icons-only {
-	width: 3.4em;
-}
-button.ui-button-icons-only {
-	width: 3.7em;
-}
-
-/* button text element */
-.ui-button .ui-button-text {
-	display: block;
-	line-height: normal;
-}
-.ui-button-text-only .ui-button-text {
-	padding: .4em 1em;
-}
-.ui-button-icon-only .ui-button-text,
-.ui-button-icons-only .ui-button-text {
-	padding: .4em;
-	text-indent: -9999999px;
-}
-.ui-button-text-icon-primary .ui-button-text,
-.ui-button-text-icons .ui-button-text {
-	padding: .4em 1em .4em 2.1em;
-}
-.ui-button-text-icon-secondary .ui-button-text,
-.ui-button-text-icons .ui-button-text {
-	padding: .4em 2.1em .4em 1em;
-}
-.ui-button-text-icons .ui-button-text {
-	padding-left: 2.1em;
-	padding-right: 2.1em;
-}
-/* no icon support for input elements, provide padding by default */
-input.ui-button {
-	padding: .4em 1em;
-}
-
-/* button icon element(s) */
-.ui-button-icon-only .ui-icon,
-.ui-button-text-icon-primary .ui-icon,
-.ui-button-text-icon-secondary .ui-icon,
-.ui-button-text-icons .ui-icon,
-.ui-button-icons-only .ui-icon {
-	position: absolute;
-	top: 50%;
-	margin-top: -8px;
-}
-.ui-button-icon-only .ui-icon {
-	left: 50%;
-	margin-left: -8px;
-}
-.ui-button-text-icon-primary .ui-button-icon-primary,
-.ui-button-text-icons .ui-button-icon-primary,
-.ui-button-icons-only .ui-button-icon-primary {
-	left: .5em;
-}
-.ui-button-text-icon-secondary .ui-button-icon-secondary,
-.ui-button-text-icons .ui-button-icon-secondary,
-.ui-button-icons-only .ui-button-icon-secondary {
-	right: .5em;
-}
-
-/* button sets */
-.ui-buttonset {
-	margin-right: 7px;
-}
-.ui-buttonset .ui-button {
-	margin-left: 0;
-	margin-right: -.3em;
-}
-
-/* workarounds */
-/* reset extra padding in Firefox, see h5bp.com/l */
-input.ui-button::-moz-focus-inner,
-button.ui-button::-moz-focus-inner {
-	border: 0;
-	padding: 0;
-}
-.ui-datepicker {
-	width: 17em;
-	padding: .2em .2em 0;
-	display: none;
-}
-.ui-datepicker .ui-datepicker-header {
-	position: relative;
-	padding: .2em 0;
-}
-.ui-datepicker .ui-datepicker-prev,
-.ui-datepicker .ui-datepicker-next {
-	position: absolute;
-	top: 2px;
-	width: 1.8em;
-	height: 1.8em;
-}
-.ui-datepicker .ui-datepicker-prev-hover,
-.ui-datepicker .ui-datepicker-next-hover {
-	top: 1px;
-}
-.ui-datepicker .ui-datepicker-prev {
-	left: 2px;
-}
-.ui-datepicker .ui-datepicker-next {
-	right: 2px;
-}
-.ui-datepicker .ui-datepicker-prev-hover {
-	left: 1px;
-}
-.ui-datepicker .ui-datepicker-next-hover {
-	right: 1px;
-}
-.ui-datepicker .ui-datepicker-prev span,
-.ui-datepicker .ui-datepicker-next span {
-	display: block;
-	position: absolute;
-	left: 50%;
-	margin-left: -8px;
-	top: 50%;
-	margin-top: -8px;
-}
-.ui-datepicker .ui-datepicker-title {
-	margin: 0 2.3em;
-	line-height: 1.8em;
-	text-align: center;
-}
-.ui-datepicker .ui-datepicker-title select {
-	font-size: 1em;
-	margin: 1px 0;
-}
-.ui-datepicker select.ui-datepicker-month-year {
-	width: 100%;
-}
-.ui-datepicker select.ui-datepicker-month,
-.ui-datepicker select.ui-datepicker-year {
-	width: 49%;
-}
-.ui-datepicker table {
-	width: 100%;
-	font-size: .9em;
-	border-collapse: collapse;
-	margin: 0 0 .4em;
-}
-.ui-datepicker th {
-	padding: .7em .3em;
-	text-align: center;
-	font-weight: bold;
-	border: 0;
-}
-.ui-datepicker td {
-	border: 0;
-	padding: 1px;
-}
-.ui-datepicker td span,
-.ui-datepicker td a {
-	display: block;
-	padding: .2em;
-	text-align: right;
-	text-decoration: none;
-}
-.ui-datepicker .ui-datepicker-buttonpane {
-	background-image: none;
-	margin: .7em 0 0 0;
-	padding: 0 .2em;
-	border-left: 0;
-	border-right: 0;
-	border-bottom: 0;
-}
-.ui-datepicker .ui-datepicker-buttonpane button {
-	float: right;
-	margin: .5em .2em .4em;
-	cursor: pointer;
-	padding: .2em .6em .3em .6em;
-	width: auto;
-	overflow: visible;
-}
-.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
-	float: left;
-}
-
-/* with multiple calendars */
-.ui-datepicker.ui-datepicker-multi {
-	width: auto;
-}
-.ui-datepicker-multi .ui-datepicker-group {
-	float: left;
-}
-.ui-datepicker-multi .ui-datepicker-group table {
-	width: 95%;
-	margin: 0 auto .4em;
-}
-.ui-datepicker-multi-2 .ui-datepicker-group {
-	width: 50%;
-}
-.ui-datepicker-multi-3 .ui-datepicker-group {
-	width: 33.3%;
-}
-.ui-datepicker-multi-4 .ui-datepicker-group {
-	width: 25%;
-}
-.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
-.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
-	border-left-width: 0;
-}
-.ui-datepicker-multi .ui-datepicker-buttonpane {
-	clear: left;
-}
-.ui-datepicker-row-break {
-	clear: both;
-	width: 100%;
-	font-size: 0;
-}
-
-/* RTL support */
-.ui-datepicker-rtl {
-	direction: rtl;
-}
-.ui-datepicker-rtl .ui-datepicker-prev {
-	right: 2px;
-	left: auto;
-}
-.ui-datepicker-rtl .ui-datepicker-next {
-	left: 2px;
-	right: auto;
-}
-.ui-datepicker-rtl .ui-datepicker-prev:hover {
-	right: 1px;
-	left: auto;
-}
-.ui-datepicker-rtl .ui-datepicker-next:hover {
-	left: 1px;
-	right: auto;
-}
-.ui-datepicker-rtl .ui-datepicker-buttonpane {
-	clear: right;
-}
-.ui-datepicker-rtl .ui-datepicker-buttonpane button {
-	float: left;
-}
-.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
-.ui-datepicker-rtl .ui-datepicker-group {
-	float: right;
-}
-.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
-.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
-	border-right-width: 0;
-	border-left-width: 1px;
-}
-.ui-dialog {
-	position: absolute;
-	top: 0;
-	left: 0;
-	padding: .2em;
-	outline: 0;
-}
-.ui-dialog .ui-dialog-titlebar {
-	padding: .4em 1em;
-	position: relative;
-}
-.ui-dialog .ui-dialog-title {
-	float: left;
-	margin: .1em 0;
-	white-space: nowrap;
-	width: 90%;
-	overflow: hidden;
-	text-overflow: ellipsis;
-}
-.ui-dialog .ui-dialog-titlebar-close {
-	position: absolute;
-	right: .3em;
-	top: 50%;
-	width: 21px;
-	margin: -10px 0 0 0;
-	padding: 1px;
-	height: 20px;
-}
-.ui-dialog .ui-dialog-content {
-	position: relative;
-	border: 0;
-	padding: .5em 1em;
-	background: none;
-	overflow: auto;
-}
-.ui-dialog .ui-dialog-buttonpane {
-	text-align: left;
-	border-width: 1px 0 0 0;
-	background-image: none;
-	margin-top: .5em;
-	padding: .3em 1em .5em .4em;
-}
-.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
-	float: right;
-}
-.ui-dialog .ui-dialog-buttonpane button {
-	margin: .5em .4em .5em 0;
-	cursor: pointer;
-}
-.ui-dialog .ui-resizable-se {
-	width: 12px;
-	height: 12px;
-	right: -5px;
-	bottom: -5px;
-	background-position: 16px 16px;
-}
-.ui-draggable .ui-dialog-titlebar {
-	cursor: move;
-}
-.ui-menu {
-	list-style: none;
-	padding: 2px;
-	margin: 0;
-	display: block;
-	outline: none;
-}
-.ui-menu .ui-menu {
-	margin-top: -3px;
-	position: absolute;
-}
-.ui-menu .ui-menu-item {
-	margin: 0;
-	padding: 0;
-	width: 100%;
-}
-.ui-menu .ui-menu-divider {
-	margin: 5px -2px 5px -2px;
-	height: 0;
-	font-size: 0;
-	line-height: 0;
-	border-width: 1px 0 0 0;
-}
-.ui-menu .ui-menu-item a {
-	text-decoration: none;
-	display: block;
-	padding: 2px .4em;
-	line-height: 1.5;
-	min-height: 0; /* support: IE7 */
-	font-weight: normal;
-}
-.ui-menu .ui-menu-item a.ui-state-focus,
-.ui-menu .ui-menu-item a.ui-state-active {
-	font-weight: normal;
-	margin: -1px;
-}
-
-.ui-menu .ui-state-disabled {
-	font-weight: normal;
-	margin: .4em 0 .2em;
-	line-height: 1.5;
-}
-.ui-menu .ui-state-disabled a {
-	cursor: default;
-}
-
-/* icon support */
-.ui-menu-icons {
-	position: relative;
-}
-.ui-menu-icons .ui-menu-item a {
-	position: relative;
-	padding-left: 2em;
-}
-
-/* left-aligned */
-.ui-menu .ui-icon {
-	position: absolute;
-	top: .2em;
-	left: .2em;
-}
-
-/* right-aligned */
-.ui-menu .ui-menu-icon {
-	position: static;
-	float: right;
-}
-.ui-progressbar {
-	height: 2em;
-	text-align: left;
-	overflow: hidden;
-}
-.ui-progressbar .ui-progressbar-value {
-	margin: -1px;
-	height: 100%;
-}
-.ui-progressbar .ui-progressbar-overlay {
-	background: url("images/animated-overlay.gif");
-	height: 100%;
-	filter: alpha(opacity=25);
-	opacity: 0.25;
-}
-.ui-progressbar-indeterminate .ui-progressbar-value {
-	background-image: none;
-}
-.ui-resizable {
-	position: relative;
-}
-.ui-resizable-handle {
-	position: absolute;
-	font-size: 0.1px;
-	display: block;
-}
-.ui-resizable-disabled .ui-resizable-handle,
-.ui-resizable-autohide .ui-resizable-handle {
-	display: none;
-}
-.ui-resizable-n {
-	cursor: n-resize;
-	height: 7px;
-	width: 100%;
-	top: -5px;
-	left: 0;
-}
-.ui-resizable-s {
-	cursor: s-resize;
-	height: 7px;
-	width: 100%;
-	bottom: -5px;
-	left: 0;
-}
-.ui-resizable-e {
-	cursor: e-resize;
-	width: 7px;
-	right: -5px;
-	top: 0;
-	height: 100%;
-}
-.ui-resizable-w {
-	cursor: w-resize;
-	width: 7px;
-	left: -5px;
-	top: 0;
-	height: 100%;
-}
-.ui-resizable-se {
-	cursor: se-resize;
-	width: 12px;
-	height: 12px;
-	right: 1px;
-	bottom: 1px;
-}
-.ui-resizable-sw {
-	cursor: sw-resize;
-	width: 9px;
-	height: 9px;
-	left: -5px;
-	bottom: -5px;
-}
-.ui-resizable-nw {
-	cursor: nw-resize;
-	width: 9px;
-	height: 9px;
-	left: -5px;
-	top: -5px;
-}
-.ui-resizable-ne {
-	cursor: ne-resize;
-	width: 9px;
-	height: 9px;
-	right: -5px;
-	top: -5px;
-}
-.ui-selectable-helper {
-	position: absolute;
-	z-index: 100;
-	border: 1px dotted black;
-}
-.ui-slider {
-	position: relative;
-	text-align: left;
-}
-.ui-slider .ui-slider-handle {
-	position: absolute;
-	z-index: 2;
-	width: 1.2em;
-	height: 1.2em;
-	cursor: default;
-}
-.ui-slider .ui-slider-range {
-	position: absolute;
-	z-index: 1;
-	font-size: .7em;
-	display: block;
-	border: 0;
-	background-position: 0 0;
-}
-
-/* For IE8 - See #6727 */
-.ui-slider.ui-state-disabled .ui-slider-handle,
-.ui-slider.ui-state-disabled .ui-slider-range {
-	filter: inherit;
-}
-
-.ui-slider-horizontal {
-	height: .8em;
-}
-.ui-slider-horizontal .ui-slider-handle {
-	top: -.3em;
-	margin-left: -.6em;
-}
-.ui-slider-horizontal .ui-slider-range {
-	top: 0;
-	height: 100%;
-}
-.ui-slider-horizontal .ui-slider-range-min {
-	left: 0;
-}
-.ui-slider-horizontal .ui-slider-range-max {
-	right: 0;
-}
-
-.ui-slider-vertical {
-	width: .8em;
-	height: 100px;
-}
-.ui-slider-vertical .ui-slider-handle {
-	left: -.3em;
-	margin-left: 0;
-	margin-bottom: -.6em;
-}
-.ui-slider-vertical .ui-slider-range {
-	left: 0;
-	width: 100%;
-}
-.ui-slider-vertical .ui-slider-range-min {
-	bottom: 0;
-}
-.ui-slider-vertical .ui-slider-range-max {
-	top: 0;
-}
-.ui-spinner {
-	position: relative;
-	display: inline-block;
-	overflow: hidden;
-	padding: 0;
-	vertical-align: middle;
-}
-.ui-spinner-input {
-	border: none;
-	background: none;
-	color: inherit;
-	padding: 0;
-	margin: .2em 0;
-	vertical-align: middle;
-	margin-left: .4em;
-	margin-right: 22px;
-}
-.ui-spinner-button {
-	width: 16px;
-	height: 50%;
-	font-size: .5em;
-	padding: 0;
-	margin: 0;
-	text-align: center;
-	position: absolute;
-	cursor: default;
-	display: block;
-	overflow: hidden;
-	right: 0;
-}
-/* more specificity required here to overide default borders */
-.ui-spinner a.ui-spinner-button {
-	border-top: none;
-	border-bottom: none;
-	border-right: none;
-}
-/* vertical centre icon */
-.ui-spinner .ui-icon {
-	position: absolute;
-	margin-top: -8px;
-	top: 50%;
-	left: 0;
-}
-.ui-spinner-up {
-	top: 0;
-}
-.ui-spinner-down {
-	bottom: 0;
-}
-
-/* TR overrides */
-.ui-spinner .ui-icon-triangle-1-s {
-	/* need to fix icons sprite */
-	background-position: -65px -16px;
-}
-.ui-tabs {
-	position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
-	padding: .2em;
-}
-.ui-tabs .ui-tabs-nav {
-	margin: 0;
-	padding: .2em .2em 0;
-}
-.ui-tabs .ui-tabs-nav li {
-	list-style: none;
-	float: left;
-	position: relative;
-	top: 0;
-	margin: 1px .2em 0 0;
-	border-bottom-width: 0;
-	padding: 0;
-	white-space: nowrap;
-}
-.ui-tabs .ui-tabs-nav li a {
-	float: left;
-	padding: .5em 1em;
-	text-decoration: none;
-}
-.ui-tabs .ui-tabs-nav li.ui-tabs-active {
-	margin-bottom: -1px;
-	padding-bottom: 1px;
-}
-.ui-tabs .ui-tabs-nav li.ui-tabs-active a,
-.ui-tabs .ui-tabs-nav li.ui-state-disabled a,
-.ui-tabs .ui-tabs-nav li.ui-tabs-loading a {
-	cursor: text;
-}
-.ui-tabs .ui-tabs-nav li a, /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
-.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a {
-	cursor: pointer;
-}
-.ui-tabs .ui-tabs-panel {
-	display: block;
-	border-width: 0;
-	padding: 1em 1.4em;
-	background: none;
-}
-.ui-tooltip {
-	padding: 8px;
-	position: absolute;
-	z-index: 9999;
-	max-width: 300px;
-	-webkit-box-shadow: 0 0 5px #aaa;
-	box-shadow: 0 0 5px #aaa;
-}
-body .ui-tooltip {
-	border-width: 2px;
-}
-
-/* Component containers
-----------------------------------*/
-.ui-widget {
-	font-family: Lucida Grande,Lucida Sans,Arial,sans-serif;
-	font-size: 1.1em;
-}
-.ui-widget .ui-widget {
-	font-size: 1em;
-}
-.ui-widget input,
-.ui-widget select,
-.ui-widget textarea,
-.ui-widget button {
-/*	font-family: Lucida Grande,Lucida Sans,Arial,sans-serif;
-	font-size: 1em;*/
-}
-.ui-widget-content {
-	border: 1px solid #a6c9e2;
-	background: #fcfdfd url(images/ui-bg_inset-hard_100_fcfdfd_1x100.png) 50% bottom repeat-x;
-	color: #222222;
-}
-.ui-widget-content a {
-	color: #222222;
-}
-.ui-widget-header {
-	border: 1px solid #4297d7;
-	background: #5c9ccc url(images/ui-bg_gloss-wave_55_5c9ccc_500x100.png) 50% 50% repeat-x;
-	color: #ffffff;
-	font-weight: bold;
-}
-.ui-widget-header a {
-	color: #ffffff;
-}
-
-/* Interaction states
-----------------------------------*/
-.ui-state-default,
-.ui-widget-content .ui-state-default,
-.ui-widget-header .ui-state-default {
-	border: 1px solid #c5dbec;
-	background: #dfeffc url(images/ui-bg_glass_85_dfeffc_1x400.png) 50% 50% repeat-x;
-	font-weight: bold;
-	color: #2e6e9e;
-}
-.ui-state-default a,
-.ui-state-default a:link,
-.ui-state-default a:visited {
-	color: #2e6e9e;
-	text-decoration: none;
-}
-.ui-state-hover,
-.ui-widget-content .ui-state-hover,
-.ui-widget-header .ui-state-hover,
-.ui-state-focus,
-.ui-widget-content .ui-state-focus,
-.ui-widget-header .ui-state-focus {
-	border: 1px solid #79b7e7;
-	background: #d0e5f5 url(images/ui-bg_glass_75_d0e5f5_1x400.png) 50% 50% repeat-x;
-	font-weight: bold;
-	color: #1d5987;
-}
-.ui-state-hover a,
-.ui-state-hover a:hover,
-.ui-state-hover a:link,
-.ui-state-hover a:visited {
-	color: #1d5987;
-	text-decoration: none;
-}
-.ui-state-active,
-.ui-widget-content .ui-state-active,
-.ui-widget-header .ui-state-active {
-	border: 1px solid #79b7e7;
-	background: #f5f8f9 url(images/ui-bg_inset-hard_100_f5f8f9_1x100.png) 50% 50% repeat-x;
-	font-weight: bold;
-	color: #e17009;
-}
-.ui-state-active a,
-.ui-state-active a:link,
-.ui-state-active a:visited {
-	color: #e17009;
-	text-decoration: none;
-}
-
-/* Interaction Cues
-----------------------------------*/
-.ui-state-highlight,
-.ui-widget-content .ui-state-highlight,
-.ui-widget-header .ui-state-highlight {
-	border: 1px solid #fad42e;
-	background: #fbec88 url(images/ui-bg_flat_55_fbec88_40x100.png) 50% 50% repeat-x;
-	color: #363636;
-}
-.ui-state-highlight a,
-.ui-widget-content .ui-state-highlight a,
-.ui-widget-header .ui-state-highlight a {
-	color: #363636;
-}
-.ui-state-error,
-.ui-widget-content .ui-state-error,
-.ui-widget-header .ui-state-error {
-	border: 1px solid #cd0a0a;
-	background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x;
-	color: #cd0a0a;
-}
-.ui-state-error a,
-.ui-widget-content .ui-state-error a,
-.ui-widget-header .ui-state-error a {
-	color: #cd0a0a;
-}
-.ui-state-error-text,
-.ui-widget-content .ui-state-error-text,
-.ui-widget-header .ui-state-error-text {
-	color: #cd0a0a;
-}
-.ui-priority-primary,
-.ui-widget-content .ui-priority-primary,
-.ui-widget-header .ui-priority-primary {
-	font-weight: bold;
-}
-.ui-priority-secondary,
-.ui-widget-content .ui-priority-secondary,
-.ui-widget-header .ui-priority-secondary {
-	opacity: .7;
-	filter:Alpha(Opacity=70);
-	font-weight: normal;
-}
-.ui-state-disabled,
-.ui-widget-content .ui-state-disabled,
-.ui-widget-header .ui-state-disabled {
-	opacity: .35;
-	filter:Alpha(Opacity=35);
-	background-image: none;
-}
-.ui-state-disabled .ui-icon {
-	filter:Alpha(Opacity=35); /* For IE8 - See #6059 */
-}
-
-/* Icons
-----------------------------------*/
-
-/* states and images */
-.ui-icon {
-	width: 16px;
-	height: 16px;
-}
-.ui-icon,
-.ui-widget-content .ui-icon {
-	background-image: url(images/ui-icons_469bdd_256x240.png);
-}
-.ui-widget-header .ui-icon {
-	background-image: url(images/ui-icons_d8e7f3_256x240.png);
-}
-.ui-state-default .ui-icon {
-	background-image: url(images/ui-icons_6da8d5_256x240.png);
-}
-.ui-state-hover .ui-icon,
-.ui-state-focus .ui-icon {
-	background-image: url(images/ui-icons_217bc0_256x240.png);
-}
-.ui-state-active .ui-icon {
-	background-image: url(images/ui-icons_f9bd01_256x240.png);
-}
-.ui-state-highlight .ui-icon {
-	background-image: url(images/ui-icons_2e83ff_256x240.png);
-}
-.ui-state-error .ui-icon,
-.ui-state-error-text .ui-icon {
-	background-image: url(images/ui-icons_cd0a0a_256x240.png);
-}
-
-/* positioning */
-.ui-icon-blank { background-position: 16px 16px; }
-.ui-icon-carat-1-n { background-position: 0 0; }
-.ui-icon-carat-1-ne { background-position: -16px 0; }
-.ui-icon-carat-1-e { background-position: -32px 0; }
-.ui-icon-carat-1-se { background-position: -48px 0; }
-.ui-icon-carat-1-s { background-position: -64px 0; }
-.ui-icon-carat-1-sw { background-position: -80px 0; }
-.ui-icon-carat-1-w { background-position: -96px 0; }
-.ui-icon-carat-1-nw { background-position: -112px 0; }
-.ui-icon-carat-2-n-s { background-position: -128px 0; }
-.ui-icon-carat-2-e-w { background-position: -144px 0; }
-.ui-icon-triangle-1-n { background-position: 0 -16px; }
-.ui-icon-triangle-1-ne { background-position: -16px -16px; }
-.ui-icon-triangle-1-e { background-position: -32px -16px; }
-.ui-icon-triangle-1-se { background-position: -48px -16px; }
-.ui-icon-triangle-1-s { background-position: -64px -16px; }
-.ui-icon-triangle-1-sw { background-position: -80px -16px; }
-.ui-icon-triangle-1-w { background-position: -96px -16px; }
-.ui-icon-triangle-1-nw { background-position: -112px -16px; }
-.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
-.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
-.ui-icon-arrow-1-n { background-position: 0 -32px; }
-.ui-icon-arrow-1-ne { background-position: -16px -32px; }
-.ui-icon-arrow-1-e { background-position: -32px -32px; }
-.ui-icon-arrow-1-se { background-position: -48px -32px; }
-.ui-icon-arrow-1-s { background-position: -64px -32px; }
-.ui-icon-arrow-1-sw { background-position: -80px -32px; }
-.ui-icon-arrow-1-w { background-position: -96px -32px; }
-.ui-icon-arrow-1-nw { background-position: -112px -32px; }
-.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
-.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
-.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
-.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
-.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
-.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
-.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
-.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
-.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
-.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
-.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
-.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
-.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
-.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
-.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
-.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
-.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
-.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
-.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
-.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
-.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
-.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
-.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
-.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
-.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
-.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
-.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
-.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
-.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
-.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
-.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
-.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
-.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
-.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
-.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
-.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
-.ui-icon-arrow-4 { background-position: 0 -80px; }
-.ui-icon-arrow-4-diag { background-position: -16px -80px; }
-.ui-icon-extlink { background-position: -32px -80px; }
-.ui-icon-newwin { background-position: -48px -80px; }
-.ui-icon-refresh { background-position: -64px -80px; }
-.ui-icon-shuffle { background-position: -80px -80px; }
-.ui-icon-transfer-e-w { background-position: -96px -80px; }
-.ui-icon-transferthick-e-w { background-position: -112px -80px; }
-.ui-icon-folder-collapsed { background-position: 0 -96px; }
-.ui-icon-folder-open { background-position: -16px -96px; }
-.ui-icon-document { background-position: -32px -96px; }
-.ui-icon-document-b { background-position: -48px -96px; }
-.ui-icon-note { background-position: -64px -96px; }
-.ui-icon-mail-closed { background-position: -80px -96px; }
-.ui-icon-mail-open { background-position: -96px -96px; }
-.ui-icon-suitcase { background-position: -112px -96px; }
-.ui-icon-comment { background-position: -128px -96px; }
-.ui-icon-person { background-position: -144px -96px; }
-.ui-icon-print { background-position: -160px -96px; }
-.ui-icon-trash { background-position: -176px -96px; }
-.ui-icon-locked { background-position: -192px -96px; }
-.ui-icon-unlocked { background-position: -208px -96px; }
-.ui-icon-bookmark { background-position: -224px -96px; }
-.ui-icon-tag { background-position: -240px -96px; }
-.ui-icon-home { background-position: 0 -112px; }
-.ui-icon-flag { background-position: -16px -112px; }
-.ui-icon-calendar { background-position: -32px -112px; }
-.ui-icon-cart { background-position: -48px -112px; }
-.ui-icon-pencil { background-position: -64px -112px; }
-.ui-icon-clock { background-position: -80px -112px; }
-.ui-icon-disk { background-position: -96px -112px; }
-.ui-icon-calculator { background-position: -112px -112px; }
-.ui-icon-zoomin { background-position: -128px -112px; }
-.ui-icon-zoomout { background-position: -144px -112px; }
-.ui-icon-search { background-position: -160px -112px; }
-.ui-icon-wrench { background-position: -176px -112px; }
-.ui-icon-gear { background-position: -192px -112px; }
-.ui-icon-heart { background-position: -208px -112px; }
-.ui-icon-star { background-position: -224px -112px; }
-.ui-icon-link { background-position: -240px -112px; }
-.ui-icon-cancel { background-position: 0 -128px; }
-.ui-icon-plus { background-position: -16px -128px; }
-.ui-icon-plusthick { background-position: -32px -128px; }
-.ui-icon-minus { background-position: -48px -128px; }
-.ui-icon-minusthick { background-position: -64px -128px; }
-.ui-icon-close { background-position: -80px -128px; }
-.ui-icon-closethick { background-position: -96px -128px; }
-.ui-icon-key { background-position: -112px -128px; }
-.ui-icon-lightbulb { background-position: -128px -128px; }
-.ui-icon-scissors { background-position: -144px -128px; }
-.ui-icon-clipboard { background-position: -160px -128px; }
-.ui-icon-copy { background-position: -176px -128px; }
-.ui-icon-contact { background-position: -192px -128px; }
-.ui-icon-image { background-position: -208px -128px; }
-.ui-icon-video { background-position: -224px -128px; }
-.ui-icon-script { background-position: -240px -128px; }
-.ui-icon-alert { background-position: 0 -144px; }
-.ui-icon-info { background-position: -16px -144px; }
-.ui-icon-notice { background-position: -32px -144px; }
-.ui-icon-help { background-position: -48px -144px; }
-.ui-icon-check { background-position: -64px -144px; }
-.ui-icon-bullet { background-position: -80px -144px; }
-.ui-icon-radio-on { background-position: -96px -144px; }
-.ui-icon-radio-off { background-position: -112px -144px; }
-.ui-icon-pin-w { background-position: -128px -144px; }
-.ui-icon-pin-s { background-position: -144px -144px; }
-.ui-icon-play { background-position: 0 -160px; }
-.ui-icon-pause { background-position: -16px -160px; }
-.ui-icon-seek-next { background-position: -32px -160px; }
-.ui-icon-seek-prev { background-position: -48px -160px; }
-.ui-icon-seek-end { background-position: -64px -160px; }
-.ui-icon-seek-start { background-position: -80px -160px; }
-/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
-.ui-icon-seek-first { background-position: -80px -160px; }
-.ui-icon-stop { background-position: -96px -160px; }
-.ui-icon-eject { background-position: -112px -160px; }
-.ui-icon-volume-off { background-position: -128px -160px; }
-.ui-icon-volume-on { background-position: -144px -160px; }
-.ui-icon-power { background-position: 0 -176px; }
-.ui-icon-signal-diag { background-position: -16px -176px; }
-.ui-icon-signal { background-position: -32px -176px; }
-.ui-icon-battery-0 { background-position: -48px -176px; }
-.ui-icon-battery-1 { background-position: -64px -176px; }
-.ui-icon-battery-2 { background-position: -80px -176px; }
-.ui-icon-battery-3 { background-position: -96px -176px; }
-.ui-icon-circle-plus { background-position: 0 -192px; }
-.ui-icon-circle-minus { background-position: -16px -192px; }
-.ui-icon-circle-close { background-position: -32px -192px; }
-.ui-icon-circle-triangle-e { background-position: -48px -192px; }
-.ui-icon-circle-triangle-s { background-position: -64px -192px; }
-.ui-icon-circle-triangle-w { background-position: -80px -192px; }
-.ui-icon-circle-triangle-n { background-position: -96px -192px; }
-.ui-icon-circle-arrow-e { background-position: -112px -192px; }
-.ui-icon-circle-arrow-s { background-position: -128px -192px; }
-.ui-icon-circle-arrow-w { background-position: -144px -192px; }
-.ui-icon-circle-arrow-n { background-position: -160px -192px; }
-.ui-icon-circle-zoomin { background-position: -176px -192px; }
-.ui-icon-circle-zoomout { background-position: -192px -192px; }
-.ui-icon-circle-check { background-position: -208px -192px; }
-.ui-icon-circlesmall-plus { background-position: 0 -208px; }
-.ui-icon-circlesmall-minus { background-position: -16px -208px; }
-.ui-icon-circlesmall-close { background-position: -32px -208px; }
-.ui-icon-squaresmall-plus { background-position: -48px -208px; }
-.ui-icon-squaresmall-minus { background-position: -64px -208px; }
-.ui-icon-squaresmall-close { background-position: -80px -208px; }
-.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
-.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
-.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
-.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
-.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
-.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
-
-
-/* Misc visuals
-----------------------------------*/
-
-/* Corner radius */
-.ui-corner-all,
-.ui-corner-top,
-.ui-corner-left,
-.ui-corner-tl {
-	border-top-left-radius: 5px;
-}
-.ui-corner-all,
-.ui-corner-top,
-.ui-corner-right,
-.ui-corner-tr {
-	border-top-right-radius: 5px;
-}
-.ui-corner-all,
-.ui-corner-bottom,
-.ui-corner-left,
-.ui-corner-bl {
-	border-bottom-left-radius: 5px;
-}
-.ui-corner-all,
-.ui-corner-bottom,
-.ui-corner-right,
-.ui-corner-br {
-	border-bottom-right-radius: 5px;
-}
-
-/* Overlays */
-.ui-widget-overlay {
-	background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;
-	opacity: .3;
-	filter: Alpha(Opacity=30);
-}
-.ui-widget-shadow {
-	margin: -8px 0 0 -8px;
-	padding: 8px;
-	background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;
-	opacity: .3;
-	filter: Alpha(Opacity=30);
-	border-radius: 8px;
-}
diff --git a/templates/builder.html b/templates/builder.html
new file mode 100644
index 0000000..bf1f1ca
--- /dev/null
+++ b/templates/builder.html
@@ -0,0 +1,36 @@
+{% extends "base.html" %}
+
+{% block title %}{{builder.name}}{% endblock %}
+
+{% block content %}
+    <h1>{{builder.name}}</h1>
+    <div class='block'>
+        <div class='desc_line'>
+            <div class='desc_key'>Name</div>
+            <div class='desc_value'>{{builder.name}}</div>
+        </div>
+        <div class='desc_line'>
+            <div class='desc_key'>Maintainer</div>
+            <div class='desc_value'><a href='{{info.maintainer_link}}'>{{builder.maintainer.name}}</a></div>
+        </div>
+        <div class='desc_line'>
+            <div class='desc_key'>PGP Fingerprint</div>
+            <div class='desc_value'>{{builder.pgp}}</div>
+        </div>
+        <div class='desc_line'>
+            <div class='desc_key'>SSL Fingerprint</div>
+            <div class='desc_value'>{{builder.ssl}}</div>
+        </div>
+        <div class='desc_line'>
+            <div class='desc_key'>Last Ping</div>
+            <div class='desc_value'>{{machine.last_ping|ago}}</div>
+        </div>
+    </div>
+
+    <h3>Jobs Assigned to {{builder.name}}</h3>
+    {% if info.prev_link %}<a class='left' href='{{info.prev_link}}'>previous page</a>{% endif %}
+    {% if info.next_link %}<a class='right' href='{{info.next_link}}'>next page</a>{% endif %}
+    <div class = 'clear' ></div>
+    {% include "job_list_fragment.html" %}
+
+{% endblock %}
diff --git a/templates/builder_list_fragment.html b/templates/builder_list_fragment.html
new file mode 100644
index 0000000..86e49a7
--- /dev/null
+++ b/templates/builder_list_fragment.html
@@ -0,0 +1,37 @@
+    <table class = 'zebra'>
+        <tr>
+            <th>Name</th>
+            <th>Maintainer</th>
+            <th>Last Ping</th>
+            <th>Current Jobs</th>
+        </tr>
+
+{% for info in builders_info %}
+        <tr>
+            <td>
+                <a href='{{info.builder_link}}'>{{info.builder.name}}</a>
+            </td>
+            <td>
+                {% if info.maintainer_link %}
+                    <a href='{{info.maintainer_link}}'>{{info.builder.maintainer.name}}</a>
+                {% else %}
+                    {{info.builder.maintainer.name}}
+                {% endif %}
+            </td>
+            <td>
+                {{info.builder.last_ping|ago}}
+            </td>
+            <td>
+                {% if info.jobs_info %}
+                    {% for jobinfo in info.jobs_info %}
+                        <a href='{{jobinfo.source_link}}'>{{jobinfo.job.source.name}}/{{jobinfo.job.source.version}}</a>
+                        <a href='{{jobinfo.job_link}}'>{{jobinfo.job.name}}</a><br />
+                    {% endfor %}
+                {% else %}
+                    None-Assigned
+                {% endif %}
+            </td>
+        </tr>
+{% endfor %}
+
+    </table>
diff --git a/templates/group.html b/templates/group.html
index 53fb5e5..534af39 100644
--- a/templates/group.html
+++ b/templates/group.html
@@ -1,13 +1,58 @@
 {% extends "base.html" %}
 
-{% block title %}Package List{% endblock %}
+{% block title %}Package Group {{group.name}}{% endblock %}
 
 {% block content %}
-<h1>Package Group {{group_id}}</h1>
-{% if page > 0 %}
-    <a class = 'left' href = '/group/{{group_id}}/{{page - 1}}' >last page</a>
-{% endif %}
-    <a class = 'right' href = '/group/{{group_id}}/{{page + 1}}' >next page</a>
+    <h1>Package Group {{group.name}}</h1>
+    <div class='block'>
+        <div class='desc_line'>
+            <div class='desc_key'>Maintainer</div>
+            <div class='desc_value'><a href='{{info.maintainer_link}}'>{{group.maintainer.name}}</a></div>
+        </div>
+        <div class='desc_line'>
+            <div class='desc_key'>Repository</div>
+            <div class='desc_value'><a href='{{group.repo_url}}'>{{group.repo_url}}</a></div>
+        </div>
+        <div class='desc_line'>
+            <div class='desc_key'>Suites:</div>
+            <div class='desc_value'>
+            {% for gs in group.suites %}
+                {{gs.suite.name}}
+            {% endfor %}
+            </div>
+        </div>
+        {% for gs in group.suites %}
+            <div class='desc_line'>
+                <div class='desc_key'>{{gs.suite.name}} Components</div>
+                <div class='desc_value'>
+                    {% for component in gs.components %}
+                        {{component.name}}
+                    {% endfor %}
+                </div>
+            </div>
+            <div class='desc_line'>
+                <div class='desc_key'>{{gs.suite.name}} Architectures</div>
+                <div class='desc_value'>
+                    {% for arch in gs.arches %}
+                        {{arch.name}}
+                    {% endfor %}
+                </div>
+            </div>
+            <div class='desc_line'>
+                <div class='desc_key'>{{gs.suite.name}} Checks</div>
+                <div class='desc_value'>
+                    {% for check in gs.checks %}
+                        {{check.name}}
+                    {% endfor %}
+                </div>
+            </div>
+        {% endfor %}
+    </div>
+
+    <h3>Sources in Package Group {{grop.name}}</h3>
+    {% if info.prev_link %}<a class='left' href='{{info.prev_link}}'>previous page</a>{% endif %}
+    {% if info.next_link %}<a class='right' href='{{info.next_link}}'>next page</a>{% endif %}
     <div class = 'clear' ></div>
     {% include "source_list_fragment.html" %}
+
 {% endblock %}
diff --git a/templates/group_list_fragment.html b/templates/group_list_fragment.html
new file mode 100644
index 0000000..b2ee118
--- /dev/null
+++ b/templates/group_list_fragment.html
@@ -0,0 +1,28 @@
+    <table class = 'zebra'>
+        <tr>
+            <th>Group Name</th>
+            <th>Suites</th>
+            <th>Maintainer</th>
+        </tr>
+
+{% for info in groups_info %}
+        <tr>
+            <td>
+                <a href='{{info.group_link}}'>{{info.group.name}}</a>
+            </td>
+            <td>
+                {% for gs in info.group.suites %}
+                    {{gs.suite.name}}
+                {% endfor %}
+            </td>
+            <td>
+                {% if info.maintainer_link %}
+                    <a href='{{info.maintainer_link}}'>{{info.group.maintainer.name}}</a>
+                {% else %}
+                    {{info.group.maintainer.name}}
+                {% endif %}
+            </td>
+        </tr>
+{% endfor %}
+
+    </table>
diff --git a/templates/index.html b/templates/index.html
index e35b324..911e38e 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1,31 +1,29 @@
 {% extends "base.html" %}
 
-{% import "macros.inc.html" as macros %}
-
-{% block title %}About{% endblock %}
+{% block title %}Debile Web{% endblock %}
 
 {% block content %}
+<h1>Groups</h1>
+{% include "group_list_fragment.html" %}
 
-This experimental infrastructure aims to provide a generic rebuild platform.<br />
-Normal build, custom builds (clang based) or static analyzers (coccinelle, scan-build, etc) are managed through this infrastructure.<br /><br />
-
-<b>Work in progress</b>
-{% include "search.html" %}
-<br />
-
-<h1>Active Jobs</h1>
+<h1>Builders</h1>
+{% include "builder_list_fragment.html" %}
 
-    {% include "job_list_fragment.html" %}
-
-<h1>Pending jobs</h1><br />
+<h1>Jobs</h1>
+List all <a href='/jobs/incomplete'>incomplete jobs</a><br />
+List all <a href='/jobs/recent'>recent jobs</a><br />
 There are currently {{pending_jobs}} jobs to assign.<br />
-<br />
-Browse by prefix:
-{{ macros.render_packages_prefixes(packages_prefixes) }}
-<br />
-<br />Last 
-<a href = '/sources' >uploads</a>
 
+<h1>Sources</h1>
+List all <a href='/sources/incomplete'>incomplete uploads</a><br />
+List all <a href='/sources/recent'>recent uploads</a><br />
+
+<h3>Browse Sources by Prefix:</h3>
+{% for prefix in prefixes %}
+    <a href="/sources/{{prefix}}">{{prefix}}</a>
+{% endfor %}
 
+<h3>Search Sources</h3>
+{% include "search.html" %}
 
 {% endblock %}
diff --git a/templates/job.html b/templates/job.html
new file mode 100644
index 0000000..6a0ba8c
--- /dev/null
+++ b/templates/job.html
@@ -0,0 +1,103 @@
+{% extends "base.html" %}
+
+{% block title %}{{job.source.name}}/{{job.source.version}} {{job.name}}{% endblock %}
+
+{% block content %}
+
+    <div class='block'>
+        <h1>{{job.source.name}}/{{job.source.version}} {{job.name}}</h1>
+        <div class='desc_line'>
+            <div class='desc_key'>Source</div>
+            <div class='desc_value'><a href='{{info.source_link}}'>{{job.source.name}}/{{job.source.version}}</a></div>
+        </div>
+        {% if job.binary %}
+            <div class='desc_line'>
+                <div class='desc_key'>Binary</div>
+                <div class='desc_value'><a href='{{info.binary_link}}'>{{job.binary.name}}/{{job.binary.version}}</a></div>
+            </div>
+        {% endif %}
+        <div class='desc_line'>
+            <div class='desc_key'>Job</div>
+            <div class='desc_value'>{{job.name}}</div>
+        </div>
+        <div class='desc_line'>
+            <div class='desc_key'>Group</div>
+            <div class='desc_value'><a href='{{info.group_link}}'>{{job.group.name}}</a></div>
+        <div>
+        <div class='desc_line'>
+            <div class='desc_key'>Suite</div>
+            <div class='desc_value'>{{job.suite.name}}</div>
+        </div>
+        <div class='desc_line'>
+            <div class='desc_key'>Component</div>
+            <div class='desc_value'>{{job.component.name}}</div>
+        </div>
+        <div class='desc_line'>
+            <div class='desc_key'>Check</div>
+            <div class='desc_value'>{{job.check.name}}</div>
+        </div>
+        <div class='desc_line'>
+            <div class='desc_key'>Architecture</div>
+            <div class='desc_value'>{{job.arch.name}}</div>
+        </div>
+        <div class='desc_line'>
+            <div class='desc_key'>Arch affinity</div>
+            <div class='desc_value'>{{job.affinity.name}}</div>
+        </div>
+        {% if job.builder %}
+            <div class='desc_line'>
+                <div class='desc_key'>Builder</div>
+                <div class='desc_value'><a href='{{info.builder_link}}'>{{job.builder.name}}</a></div>
+            </div>
+            <div class='desc_line'>
+                <div class='desc_key'>Build time</div>
+                <div class='desc_value'>{{info.job_runtime}}</div>
+            </div>
+        {% endif %}
+        <div class='desc_line'>
+            <div class='desc_key'>Status</div>
+            <div class='desc_value'>
+                {% if job.finished_at %}
+                    {% if job.check.build %}
+                        {# Builds #}
+                        {% if job.failed %}Failed{% else %}Uploaded{% endif %}
+                    {% else %}
+                        {# Analyzers #}
+                        {% if job.failed %}✗ Errors found{% else %}✓ No errors found{% endif %}
+                    {% endif %}
+                {% else %}
+                    Needs-Build
+                {% endif %}
+            </div>
+        </div>
+    </div>
+
+    {% if job.finished_at %}
+        <div class='block'>
+            <h3>Downloads</h3>
+            <div class='desc_line'>
+                <div class='desc_key'>Uploaded dud</div>
+                <div class='desc_value'><a href='{{info.dud_link}}'>{{info.dud_name}}</a></div>
+            </div>
+            <div class='desc_line'>
+                <div class='desc_key'>Job Log</div>
+                <div class='desc_value'><a href='{{info.log_link}}'>{{info.log_name}}</a></div>
+            </div>
+            <div class='desc_line'>
+                <div class='desc_key'>Firehose Report</div>
+                <div class='desc_value'><a href='{{info.firehose_link}}'>{{info.firehose_name}}</a></div>
+            </div>
+            {% if info.files %}
+                <div class='desc_line'>
+                    <div class='desc_key'>Additional Files</div>
+                    <div class='desc_value'>
+                        {% for file in info.files %}
+                            <a href='{{job.files_url}}/{{file}}'>{{file}}</a><br />
+                        {% endfor %}
+                    </div>
+                </div>
+            {% endif %}
+        </div>
+    {% endif %}
+
+{% endblock %}
diff --git a/templates/job_list_fragment.html b/templates/job_list_fragment.html
index 865c91d..07c3319 100644
--- a/templates/job_list_fragment.html
+++ b/templates/job_list_fragment.html
@@ -1,28 +1,58 @@
-    <table class = 'zebra'>
+    <table>
         <tr>
-            <th>Source Package</th>
-            <th>Type</th>
-            <th>Arch</th>
+            <th>Source</th>
+            <th>Job</th>
+            <th>Group</th>
             <th>Suite</th>
-            <th>Assigned</th>
             <th>Builder</th>
+            <th>Status</th>
         </tr>
-{% for jobinfo in active_jobs_info %}
-        <tr>
+
+{% for info in jobs_info %}
+        <tr class='job {{info.status}}'>
+            <td>
+                {% if info.source_link %}
+                    <a href='{{info.source_link}}'>{{info.job.source.name}}/{{info.job.source.version}}</a>
+                {% else %}
+                    {{info.job.source.name}}/{{info.job.source.version}}
+                {% endif %}
+            </td>
+            <td>
+                <a href = '{{info.job_link}}' >{{info.job.name}}</a>
+            </td>
             <td>
-                <a href = '{{jobinfo.package_link}}' >{{jobinfo.job.package.name}}</a><br />{{jobinfo.job.package.version}}
+                {% if info.group_link %}
+                    <a href='{{info.group_link}}'>{{info.job.group.name}}</a>
+                {% else %}
+                    {{info.job.group.name}}
+                {% endif %}
+            </td>
+            <td>
+                {{info.job.suite.name}}
+            </td>
+            <td>
+                {% if info.builder_link %}
+                    <a href = '{{info.builder_link}}' >{{info.job.builder.name}}</a>
+                {% elif info.job.builder %}
+                    {{info.job.builder.name}}
+                {% else %}
+                    Not-Assigned
+                {% endif %}
             </td>
-            <td>{{jobinfo.job.type}}{% if jobinfo.job.subtype %}/{{jobinfo.job.subtype}}{% endif %}</td>
-            <td>{{jobinfo.job.arch}}</td>
-            <td>{{jobinfo.job.suite}}</td>
-            <td>{{jobinfo.job.assigned_at|ago}}</td>
             <td>
-                {% if jobinfo.job.machine %}
-                <a href = '{{jobinfo.machine_link}}' >{{jobinfo.job.machine.name}}</a>
+                {% if info.job.finished_at %}
+                    {% if info.job.check.build %}
+                        {# Builds #}
+                        {% if info.job.failed %}Failed{% else %}Uploaded{% endif %}
+                    {% else %}
+                        {# Analyzers #}
+                        {% if info.job.failed %}✗ Errors found{% else %}✓ No errors found{% endif %}
+                    {% endif %}
                 {% else %}
-                <em>not assigned</em>
+                    Needs-Build
                 {% endif %}
             </td>
         </tr>
 {% endfor %}
+
     </table>
diff --git a/templates/jobs.html b/templates/jobs.html
new file mode 100644
index 0000000..5835593
--- /dev/null
+++ b/templates/jobs.html
@@ -0,0 +1,14 @@
+{% extends "base.html" %}
+
+{% block title %}Job List{% endblock %}
+
+{% block content %}
+    <h1>Job list</h1>
+    {% if info.desc %}{{info.desc}}{% endif %}
+
+    {% if info.prev_link %}<a class='left' href='{{info.prev_link}}'>previous page</a>{% endif %}
+    {% if info.next_link %}<a class='right' href='{{info.next_link}}'>next page</a>{% endif %}
+    <div class = 'clear' ></div>
+    {% include "job_list_fragment.html" %}
+
+{% endblock %}
diff --git a/templates/machine.html b/templates/machine.html
deleted file mode 100644
index 09310d5..0000000
--- a/templates/machine.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}Machine{% endblock %}
-
-{% block content %}
-<h1>{{machine.name}}</h1>
-
-The full GPG
-fingerprint of this buildd is <code>{{machine.gpg_fingerprint}}</code>. This machine
-last hit us authenticated {{machine.last_ping|ago}}.
-{% endblock %}
diff --git a/templates/machine_list_fragment.html b/templates/machine_list_fragment.html
deleted file mode 100644
index b7f7d6a..0000000
--- a/templates/machine_list_fragment.html
+++ /dev/null
@@ -1,21 +0,0 @@
-    <table class = 'zebra'>
-        <tr>
-            <th>Name</th>
-            <th>Last ping</th>
-            <th>Jobs</th>
-        </tr>
-{% for machine in machines %}
-        <tr>
-            <td><a href = '/machine/{{machine.name}}' >{{machine.name}}</a></td>
-            <td>{{machine.last_ping|ago}}</td>
-            <td>
-                {% for job in machine.jobs%}
-				{% if not job.finished_at %}
-                    <a href = '/report/{{job.id}}' >{{job.type}}</a>
-				{% endif %}
-                {% endfor %}
-            </td>
-        </tr>
-{% endfor %}
-    </table>
-
diff --git a/templates/macros.inc.html b/templates/macros.inc.html
deleted file mode 100644
index d70c1f7..0000000
--- a/templates/macros.inc.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
-{% macro render_packages_prefixes(prefixes) %}
-  {%- for prefix in prefixes %}
-{#  <a href="{{ url_for('prefix', prefix_id=prefix) }}">{{ prefix }}</a>
-#}
-    <a href="/prefix/{{ prefix }}">{{ prefix }}</a>
-
-  {%- endfor %}
-{% endmacro %}
-
diff --git a/templates/prefix.html b/templates/prefix.html
deleted file mode 100644
index 8d46173..0000000
--- a/templates/prefix.html
+++ /dev/null
@@ -1,8 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}Package List{% endblock %}
-
-{% block content %}
-    <h1>Package list: '{{prefix}}'</h1>
-    {% include "prefix_list.html" %}
-{% endblock %}
diff --git a/templates/prefix_list.html b/templates/prefix_list.html
deleted file mode 100644
index 42ae93c..0000000
--- a/templates/prefix_list.html
+++ /dev/null
@@ -1,37 +0,0 @@
-    <table class = 'zebra'>
-        <tr>
-            <th>What</th>
-            <th>Latest version</th>
-{#            <th>Uploader</th>
-            <th>Status</th>#}
-        </tr>
-{% for info in sources %}
-        <tr>
-            <td>
-                <a href = '{{info.source_link}}' >
-{{info.source.name}}
-</td>
-<td>
-{{info.source.version}}
-</td>
-
-{#                    {{info.source.name}}/{{info.source.version}}{% if info.source.run > 1 %} - run {{info.source.run}}{% endif %}
-
-                </a>
-            </td>
-            <td>
-                {% if info.source.group %}
-                <a href = '{{info.group_link}}' >
-                    {{info.source.group.name}}
-                </a>
-                {% endif %}
-            </td>
-            <td>
-                <a href = '{{info.user_link}}' >
-                    {{info.source.user.name}}
-                </a>
-            </td>
-        </tr>
-#}
-{% endfor %}
-    </table>
diff --git a/templates/report.html b/templates/report.html
deleted file mode 100644
index 2953d69..0000000
--- a/templates/report.html
+++ /dev/null
@@ -1,35 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}Report for job id={{job_info.job.id}}{% endblock %}
-
-{% block content %}
-{% if job_info.job.package.type == 'binary' %}
-{% include 'report_desc_binary.html' %}
-{% else %}
-{% include 'report_desc_source.html' %}
-{% endif %}
-
-{% if job_info.job.failed %}
-    <div class='block'>
-	<h2>This job failed, check the reports and file/fix potential bugs.</h2>
-    </div>
-    {% if scanbuild_link %}
-    <div class='block'>
-		<h3>Scan-build output</h3>
-        <div class='desc_line'>
-            <div class='desc_key'>HTML formatted</div>
-            <div class='desc_value'><a href='{{scanbuild_link}}'>see report</a></div>
-        </div>
-    </div>
-    {% endif %}
-{% else %}
-    <div class='block'>
-	<h2>This job succeeded. No results have been found.</h2>
-    </div>
-{% endif %}
-
-<h1>Full Log</h1>
-<a name="full_log" />
-<pre>
-{% for line in log %}{{line}}{% endfor %}</pre>
-{% endblock %}
diff --git a/templates/report_desc_binary.html b/templates/report_desc_binary.html
deleted file mode 100644
index f80e7d3..0000000
--- a/templates/report_desc_binary.html
+++ /dev/null
@@ -1,44 +0,0 @@
-    <div class='block'>
-		<h3>Job info</h3>
-        <div class='desc_line'>
-            <div class='desc_key'>type</div>
-            <div class='desc_value'>{{job_info.job.type}}</div>
-        </div>
-        <div class='desc_line'>
-            <div class='desc_key'>Builder</div>
-            <div class='desc_value'><a href='{{job_info.machine_link}}'>{{job_info.job.machine.name}}</a></div>
-        </div>
-        {% if job_info.job.type == 'build' %}
-        <div class='desc_line'>
-            <div class='desc_key'>Compiler setup</div>
-            <div class='desc_value'>{{job_info.job.subtype}}</div>
-        </div>
-        {% endif %}
-        <div class='desc_line'>
-            <div class='desc_key'>Binary package</div>
-            <div class='desc_value'>{{job_info.job.package.name}} {{job_info.job.package.version}}</div>
-        </div>
-        <div class='desc_line'>
-            <div class='desc_key'>Source package</div>
-            <div class='desc_value'><a href="{{job_info.source_link}}">{{job_info.job.package.source.name}}</a></div>
-        </div>
-        <div class='desc_line'>
-            <div class='desc_key'>Build time </div>
-            <div class='desc_value'>{{job_info.job_runtime}}</div>
-        </div>
-    </div>
-    <div class='block'>
-		<h3>Downloads</h3>
-        <div class='desc_line'>
-            <div class='desc_key'>Binary .deb</div>
-            <div class='desc_value'><a href='{{job_info.deb_link}}'>{{job_info.job.package.deb}}</a></div>
-        </div>
-        <div class='desc_line'>
-            <div class='desc_key'>Job log</div>
-            <div class='desc_value'><a href='{{log_link}}'>Text format</a></div>
-        </div>
-        <div class='desc_line'>
-            <div class='desc_key'>Firehose report</div>
-            <div class='desc_value'><a href='{{firehose_link}}'>XML format</a></div>
-        </div>
-    </div>
diff --git a/templates/report_desc_source.html b/templates/report_desc_source.html
deleted file mode 100644
index 248ed81..0000000
--- a/templates/report_desc_source.html
+++ /dev/null
@@ -1,36 +0,0 @@
-    <div class='block'>
-		<h3>Job info</h3>
-        <div class='desc_line'>
-            <div class='desc_key'>type</div>
-            <div class='desc_value'>{{job_info.job.type}}</div>
-        </div>
-        {% if job_info.job.subtype %}
-        <div class='desc_line'>
-            <div class='desc_key'>subtype</div>
-            <div class='desc_value'>{{job_info.job.subtype}}</div>
-        </div>
-        {% endif %}
-        <div class='desc_line'>
-            <div class='desc_key'>builder</div>
-            <div class='desc_value'><a href='{{job_info.machine_link}}'>{{job_info.job.machine.name}}</a></div>
-        </div>
-        <div class='desc_line'>
-            <div class='desc_key'>source package</div>
-            <div class='desc_value'><a href='{{job_info.package_link}}'>{{job_info.job.package.name}} {{job_info.job.package.version}}</a></div>
-        </div>
-        <div class='desc_line'>
-            <div class='desc_key'>Build time</div>
-            <div class='desc_value'>{{job_info.job_runtime}}</div>
-        </div>
-    </div>
-    <div class='block'>
-		<h3>Downloads</h3>
-        <div class='desc_line'>
-            <div class='desc_key'>job log</div>
-            <div class='desc_value'><a href='{{log_link}}'>Text format</a></div>
-        </div>
-        <div class='desc_line'>
-            <div class='desc_key'>firehose report</div>
-            <div class='desc_value'><a href='{{firehose_link}}'>XML format</a></div>
-        </div>
-    </div>
diff --git a/templates/report_list_binary_fragment.html b/templates/report_list_binary_fragment.html
deleted file mode 100644
index 0f2a531..0000000
--- a/templates/report_list_binary_fragment.html
+++ /dev/null
@@ -1,37 +0,0 @@
-    <table>
-        <tr>
-            <th>Type</th>
-            <th>Name</th>
-            <th>Status</th>
-            <th>Machine</th>
-            <th>Results</th>
-        </tr>
-{% for jobinfo in jobinfolist %}
-        <tr class='job {% if jobinfo.job.is_finished() %}{% if jobinfo.job.failed %}failed{% else %}success{% endif %}{% endif %}'>
-            <td>
-				{% if jobinfo.job.is_finished() %}
-                <a href = '{{jobinfo.job_link}}' >{{jobinfo.job.type}}</a> <em>({{jobinfo.job.arch}})</em>
-				{% else %}
-				{{jobinfo.job.type}} <em>({{jobinfo.job.arch}})</em>
-				{% endif %}
-            </td>
-            <td>{{jobinfo.job.package.name}}</td>
-            <td>{{jobinfo.status}}</td>
-            <td>
-                {% if jobinfo.job.machine %}
-                <a href = '{{jobinfo.job_machine_link}}' >{{jobinfo.job.machine.name}}</a>
-				{% else %}
-				not assigned yet
-                {% endif %}
-            </td>
-            <td>{% if jobinfo.job.is_finished() %}{% if jobinfo.job.failed %}✗ Errors found{% else %}✓ Nothing found{% endif %}{% endif %}</td>
-        </tr>
-{% endfor %}
-{% if jobinfolist|length == 0 %}
-        <tr class='emptymessage'>
-			<td colspan='4'>
-				{{emptymessage}}
-			</td>
-		</tr>
-{% endif %}
-    </table>
diff --git a/templates/report_list_fragment.html b/templates/report_list_fragment.html
deleted file mode 100644
index 7cb33d9..0000000
--- a/templates/report_list_fragment.html
+++ /dev/null
@@ -1,52 +0,0 @@
-    <table>
-        <tr>
-            <th>Type</th>
-            <th>Version</th>
-            <th>Machine</th>
-            <th>Results</th>
-        </tr>
-{% set typebuild = '' %}
-
-{% for jobinfo in jobinfolist %}
-{# Add an empty line between the normal build + static analyzers #}
-{% if typebuildprev == "build" and typebuildprev != jobinfo.job.type %}<tr><td colspan="4"> </td></tr>{% endif %}
-
-        <tr class='job {% if jobinfo.job.is_finished() %}{% if jobinfo.job.failed %}failed{% else %}success{% endif %}{% endif %}'>
-            <td>
-                {% if jobinfo.job.is_finished() %}
-                    <a href = '{{jobinfo.job_link}}' >{{jobinfo.job.type}}{% if jobinfo.job.subtype %}/{{jobinfo.job.subtype}}{% endif %}</a> <em>({{jobinfo.job.arch}})</em>
-                {% else %}
-                {{jobinfo.job.type}}{% if jobinfo.job.subtype %}/{{jobinfo.job.subtype}}{% endif %} <em>({{jobinfo.job.arch}})</em>
-                {% endif %}
-            </td>
-            <td>{{package.version}}</td>
-            <td>
-                {% if jobinfo.job.machine %}
-                <a href = '{{jobinfo.job_machine_link}}' >{{jobinfo.job.machine.name}}</a>
-				{% else %}
-				not assigned yet
-                {% endif %}
-            </td>
-            <td>{% if jobinfo.job.is_finished() %}
-	      {% if jobinfo.job.type == "build" %} {# Build of the package package #}
-	          {% if jobinfo.job.failed %}Failed{% else %}Uploaded{% endif %}
-	      {% else %} {# Analyzers #}
-                  {% if jobinfo.job.failed %}<a href='{{jobinfo.job_report_link}}'>✗ Errors found</a>{% else %}✓ Nothing found{% endif %}
-	      {% endif %}
-	      {% else %}
-	      Needs-Build
-	      {% endif %}
-	    </td>
-
-{% set typebuildprev = jobinfo.job.type %}
-
-        </tr>
-{% endfor %}
-{% if jobinfolist|length == 0 %}
-        <tr class='emptymessage'>
-			<td colspan='4'>
-				{{emptymessage}}
-			</td>
-		</tr>
-{% endif %}
-    </table>
diff --git a/templates/repository.html b/templates/repository.html
deleted file mode 100644
index bb42f29..0000000
--- a/templates/repository.html
+++ /dev/null
@@ -1,21 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}Repository{% endblock %}
-
-{% block content %}
-<h1>{{worker.name}}</h1>
-
-{{worker.name}}'s GPG key is <code>{{worker.gpg_fingerprint}}</code>.
-
-<h2>Repositories of packages</h2>
-Choose to add one of the following mirrors to <strong>/etc/apt/sources.list.d/debile.list</strong>
-<pre>
-{{apt_binary_list}}
-</pre>
-
-<h2>Source repository</h2>
-Add the following mirror(s) to <strong>/etc/apt/sources.list.d/debile.list</strong>
-<pre>
-{{apt_source_list}}
-</pre>
-{% endblock %}
diff --git a/templates/search.html b/templates/search.html
index b9fda0e..db6af55 100644
--- a/templates/search.html
+++ b/templates/search.html
@@ -8,15 +8,15 @@
 
  <script type="text/javascript">
   $(function() {
-  $( "#package" ).autocomplete({
+  $( "#source" ).autocomplete({
     source: function( request, response ) {
-        $.getJSON($SCRIPT_ROOT + "/_search_package", {
+        $.getJSON($SCRIPT_ROOT + "/_search_source", {
           search: request
         }, response);
     },
     select: function(event, ui) { 
-      $("#package").val(ui.item.label);
-      $("#searchPackageForm").submit(); }
+      $("#source").val(ui.item.label);
+      $("#searchSourceForm").submit(); }
   })
   });
 
@@ -41,9 +41,9 @@
   }
   </style> 
 
-<table><tr><td>By package</td><td>
-<form id="searchPackageForm" action="/source/search/" method="POST">
-  <div class="ui-widget">{{form.package(size=20)}}
+<table><tr><td>By source</td><td>
+<form id="searchSourceForm" action="/source/search/" method="POST">
+  <div class="ui-widget">{{form.source(size=20)}}
   <input type="submit" value="Search" /></div>
 </form>
 </td></tr>
diff --git a/templates/source-not-found.html b/templates/source-not-found.html
index 4e83188..0c78c7c 100644
--- a/templates/source-not-found.html
+++ b/templates/source-not-found.html
@@ -4,9 +4,7 @@
 
 {% block content %}
 
-Package <i>{{ package_name }}</i> {% if version %} {{ version }} {% endif %} not found
-{% if version %}<br />
-Try the <a href="/source/fred/{{ package_name }}/latest/1">latest release</a>
-{% endif %}
+Package <i>{{package_name}}/{{version_or_suite}}</i> not found in group <i>{{group_name}}</i><br />
+You can try to search for packages named <a href="/source/{{package_name}}">{{package_name}}</a> instead.<br />
 
 {% endblock %}
diff --git a/templates/source.html b/templates/source.html
index f452a51..4d82ab3 100644
--- a/templates/source.html
+++ b/templates/source.html
@@ -1,10 +1,10 @@
 {% extends "base.html" %}
 
-{% block title %}Package {{package.name}}/{{package.version}}{% endblock %}
+{% block title %}{{source.name}}/{{source.version}}{% endblock %}
 
 {% block content %}
 
-{% with total, unfinished = package_job_status %}
+{% with total, unfinished = info.job_status %}
     <div class = 'box' >
         <div class = 'unfinished' >
             {{unfinished}}<span class = 'total' >/{{total}}</span>
@@ -13,61 +13,56 @@
     </div>
 {% endwith %}
 
-
     <div class='block'>
-		<h3>Package info</h3>
+        <h1>{{source.name}}/{{source.version}}</h1>
         <div class='desc_line'>
             <div class='desc_key'>Name</div>
-            <div class='desc_value'>{{package.name}}</div>
+            <div class='desc_value'>{{source.name}}</div>
         </div>
         <div class='desc_line'>
             <div class='desc_key'>Version</div>
-            <div class='desc_value'>{{package.version}}</div>
+            <div class='desc_value'>{{source.version}}</div>
+        </div>
+        <div class='desc_line'>
+            <div class='desc_key'>Group</div>
+            <div class='desc_value'><a href='{{info.group_link}}'>{{source.group.name}}</a></div>
         </div>
         <div class='desc_line'>
-            <div class='desc_key'>Maintainer</div>
-            <div class='desc_value'>{{package.maintainer}}</div>
+            <div class='desc_key'>Suite</div>
+            <div class='desc_value'>{{source.suite.name}}</div>
         </div>
         <div class='desc_line'>
-            <div class='desc_key'>Built by</div>
-            <div class='desc_value'><a href='{{desc.user_link}}'>{{package.user.name}}</a></div>
+            <div class='desc_key'>Component</div>
+            <div class='desc_value'>{{source.component.name}}</div>
         </div>
         <div class='desc_line'>
-            <div class='desc_key'>Available on</div>
-            <div class='desc_value'><a href='{{desc.user_repository_link}}'>Repository</a></div>
+            <div class='desc_key'>Uploader</div>
+            <div class='desc_value'><a href='{{info.uploader_link}}'>{{source.uploader.name}}</a></div>
         </div>
     </div>
 
-    {% if multiple_runs %}
     <div class='block'>
-    <h3>debile runs</h3>
-        {% if latest_run == package.run %}
-        This is the latest run of debile available for this package, but you can access previous runs:
-        {% else %}
-        This package version has been processed multiple times, see other runs:
-        {% endif %}
-        <div class='package_runs_menu'>
-            {% for run, link in runs_info %}
-            <div class='package_runs_menu_element {% if desc.run == run %}current_run{% endif %}'>
-                <a href='{{link}}'>{{run}}{% if loop.last %} (latest){% endif %}</a>
+        <h3>(Co-)Maintainers</h3>
+        <div class='package_maintainers_menu'>
+            {% for maintainer in source.maintainers %}
+            <div class='package_maintainers_menu_element'>
+                {{maintainer.name}} <{{maintainer.email}}>
             </div>
-			{% if not loop.last %} | {% endif %}
             {% endfor %}
         </div>
     </div>
-    {% endif %}
 
-    {% if multiple_versions %}
+    {% if versions_info %}
     <div class='block'>
-    <h3>Versions</h3>
+        <h3>Other Versions</h3>
         <div class='package_versions_menu'>
             {% for version, link in versions_info %}
             <div class='package_versions_menu_element'>
-	      {% if version == package.version %}
-	      {{version}}{% if loop.last %} (latest){% endif %}
-	      {% else %}
-              <a href='{{link}}'>{{version}}{% if loop.last %} (latest){% endif %}</a>
-	      {% endif %}
+                {% if version == source.version %}
+                    {{version}}
+                {% else %}
+                    <a href='{{link}}'>{{version}}</a>
+                {% endif %}
             </div>
             {% endfor %}
         </div>
@@ -75,23 +70,8 @@
     {% endif %}
 
     <div class='block'>
-    <h3>source jobs</h3>
-    launched on the source package provided
-{% with jobinfolist = source_jobs_info %}
-{% with emptymessage = 'Ahem, this is weird, there should be jobs here, call us...' %}
-    {% include "report_list_fragment.html" %}
-{% endwith %}
-{% endwith %}
-    </div>
-
-    <div class='block'>
-    <h3>binary jobs</h3>
-launched on the binaries uploaded after the build of the source package
-{% with jobinfolist = binaries_jobs_info %}
-{% with emptymessage = 'No binary jobs yet, check the build jobs above.' %}
-    {% include "report_list_binary_fragment.html" %}
-{% endwith %}
-{% endwith %}
+        <h3>Jobs</h3>
+        {% include "job_list_fragment.html" %}
     </div>
 
 {% endblock %}
diff --git a/templates/source_list.html b/templates/source_list.html
deleted file mode 100644
index 51c5452..0000000
--- a/templates/source_list.html
+++ /dev/null
@@ -1,8 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}Package List{% endblock %}
-
-{% block content %}
-    <h1>{{count}} most recently uploaded source packages</h1>
-    {% include "source_list_fragment.html" %}
-{% endblock %}
diff --git a/templates/source_list_fragment.html b/templates/source_list_fragment.html
index 9be8ca9..0d9fdb4 100644
--- a/templates/source_list_fragment.html
+++ b/templates/source_list_fragment.html
@@ -1,50 +1,52 @@
     <table class = 'zebra'>
         <tr>
-            <th>What</th>
+            <th>Source</th>
             <th>Group</th>
+            <th>Suite</th>
             <th>Uploader</th>
             <th>Status</th>
         </tr>
+
 {% for info in sources_info %}
         <tr>
             <td>
-                <a href = '{{info.source_link}}' >
-                    {{info.source.name}}/{{info.source.version}}{% if info.source.run > 1 %} - run {{info.source.run}}{% endif %}
-                </a>
+                <a href='{{info.source_link}}'>{{info.source.name}}/{{info.source.version}}</a>
             </td>
             <td>
-                {% if info.source.group %}
-                <a href = '{{info.group_link}}' >
+                {% if info.group_link %}
+                    <a href='{{info.group_link}}'>{{info.source.group.name}}</a>
+                {% else %}
                     {{info.source.group.name}}
-                </a>
                 {% endif %}
             </td>
             <td>
-                <a href = '{{info.user_link}}' >
-                    {{info.source.user.name}}
-                </a>
+                {{info.source.suite.name}}
             </td>
             <td>
-            {% for jerb in info.source.jobs %}
-                {% if jerb.is_pending() %}
-                    {% if jerb.builder %}
-                        ⧖
-                    {% else %}
-                        ⌚
-                    {% endif %}
+                {% if info.upploader_link %}
+                    <a href = '{{info.uploader_link}}' >{{info.source.uploader.name}}</a>
                 {% else %}
-                    {% if jerb.failed %}
-                        {% if jerb.is_critical() %}
-                            ☹
-                        {% else %}
+                    {{info.source.uploader.name}}
+                {% endif %}
+            </td>
+            <td>
+                {% for job in info.source.jobs %}
+                    {% if job.finished_at %}
+                        {% if job.failed %}
                             ✗
+                        {% else %}
+                            ✓
                         {% endif %}
                     {% else %}
-                        ✓
+                        {% if job.assigned_at %}
+                            ⧖
+                        {% else %}
+                            ⌚
+                        {% endif %}
                     {% endif %}
-                {% endif %}
-            {% endfor %}
+                {% endfor %}
             </td>
         </tr>
 {% endfor %}
+
     </table>
diff --git a/templates/sources.html b/templates/sources.html
new file mode 100644
index 0000000..2e654f2
--- /dev/null
+++ b/templates/sources.html
@@ -0,0 +1,14 @@
+{% extends "base.html" %}
+
+{% block title %}Package List{% endblock %}
+
+{% block content %}
+    <h1>Package list</h1>
+    {% if info.desc %}{{info.desc}}{% endif %}
+
+    {% if info.prev_link %}<a class='left' href='{{info.prev_link}}'>previous page</a>{% endif %}
+    {% if info.next_link %}<a class='right' href='{{info.next_link}}'>next page</a>{% endif %}
+    <div class = 'clear' ></div>
+    {% include "source_list_fragment.html" %}
+
+{% endblock %}
diff --git a/templates/user.html b/templates/user.html
new file mode 100644
index 0000000..623c6f7
--- /dev/null
+++ b/templates/user.html
@@ -0,0 +1,42 @@
+{% extends "base.html" %}
+
+{% block title %}{{user.name}}{% endblock %}
+
+{% block content %}
+    <h1>{{user.name}}</h1>
+    <div class='block'>
+        <div class='desc_line'>
+            <div class='desc_key'>Name</div>
+            <div class='desc_value'>{{user.name}}</div>
+        </div>
+        <div class='desc_line'>
+            <div class='desc_key'>Email</div>
+            <div class='desc_value'><a href='mailto:{{user.email}}'>{{user.email}}</a></div>
+        </div>
+        <div class='desc_line'>
+            <div class='desc_key'>PGP Fingerprint:</div>
+            <div class='desc_value'>{{user.pgp}}</div>
+        </div>
+        <div class='desc_line'>
+            <div class='desc_key'>SSL Fingerprint:</div>
+            <div class='desc_value'>{{user.ssl}}</div>
+        </div>
+    </div>
+
+    {% if groups_info %}
+        <h3>Groups Maintained by {{user.name}}</h3>
+        {% include "groups_list_fragment.html" %}
+    {%endif}
+
+    {% if builders_info %}
+        <h3>Builders Maintained by {{user.name}}</h3>
+        {% include "builder_list_fragment.html" %}
+    {%endif}
+
+    <h3>Sources Uploaded by {{user.name}}</h3>
+    {% if info.prev_link %}<a class='left' href='{{info.prev_link}}'>previous page</a>{% endif %}
+    {% if info.next_link %}<a class='right' href='{{info.next_link}}'>next page</a>{% endif %}
+    <div class = 'clear' ></div>
+    {% include "source_list_fragment.html" %}
+    
+{% endblock %}
diff --git a/templates/worker.html b/templates/worker.html
deleted file mode 100644
index af7f20d..0000000
--- a/templates/worker.html
+++ /dev/null
@@ -1,19 +0,0 @@
-{% extends "base.html" %}
-
-{% block title %}Worker{% endblock %}
-
-{% block content %}
-<h1>{{worker.name}}</h1>
-
-{{worker.name}}'s GPG key is <code>{{worker.gpg_fingerprint}}</code>.
-
-<h2>Upload your source package to debile</h2>
-Follow our one-minute-very-easy-even-for-noobs setup guide.
-<ul>
-    <li>Install dput-ng</li>
-    <li>Copy the following in your <strong>~/.dput.d/profiles/debile.json</strong></li>
-</ul>
-<pre>
-{{dput_upload_profile}}
-</pre>
-{% endblock %}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-debile/debile-web.git



More information about the Pkg-debile-commits mailing list