damned-lies r1064 - in branches/djamnedlies: . docs stats stats/conf stats/data stats/management stats/management/commands stats/templates



Author: claudep
Date: Mon Oct 20 21:34:37 2008
New Revision: 1064
URL: http://svn.gnome.org/viewvc/damned-lies?rev=1064&view=rev

Log:
Initial commit of djangoified Damned-Lies.

Added:
   branches/djamnedlies/   (props changed)
   branches/djamnedlies/README
   branches/djamnedlies/TODO
   branches/djamnedlies/__init__.py
   branches/djamnedlies/docs/
   branches/djamnedlies/docs/DataModel.odg   (contents, props changed)
   branches/djamnedlies/manage.py
   branches/djamnedlies/settings.py
   branches/djamnedlies/stats/
   branches/djamnedlies/stats/__init__.py
   branches/djamnedlies/stats/admin.py
   branches/djamnedlies/stats/conf/
   branches/djamnedlies/stats/conf/__init__.py
   branches/djamnedlies/stats/conf/settings.py
   branches/djamnedlies/stats/data/
   branches/djamnedlies/stats/data/bar.png   (contents, props changed)
   branches/djamnedlies/stats/data/cyan-bar.png   (contents, props changed)
   branches/djamnedlies/stats/data/download.png   (contents, props changed)
   branches/djamnedlies/stats/data/emptyimg.png   (contents, props changed)
   branches/djamnedlies/stats/data/error.png   (contents, props changed)
   branches/djamnedlies/stats/data/figure.png   (contents, props changed)
   branches/djamnedlies/stats/data/foot-16.png   (contents, props changed)
   branches/djamnedlies/stats/data/foot.png   (contents, props changed)
   branches/djamnedlies/stats/data/general_bg.png   (contents, props changed)
   branches/djamnedlies/stats/data/general_separator.png   (contents, props changed)
   branches/djamnedlies/stats/data/gnome-64.png   (contents, props changed)
   branches/djamnedlies/stats/data/gnome-gtp.png   (contents, props changed)
   branches/djamnedlies/stats/data/green-bar.png   (contents, props changed)
   branches/djamnedlies/stats/data/info.png   (contents, props changed)
   branches/djamnedlies/stats/data/layout.css
   branches/djamnedlies/stats/data/main.css
   branches/djamnedlies/stats/data/main.js
   branches/djamnedlies/stats/data/nobody.png   (contents, props changed)
   branches/djamnedlies/stats/data/purple-bar.png   (contents, props changed)
   branches/djamnedlies/stats/data/red-bar.png   (contents, props changed)
   branches/djamnedlies/stats/data/rtl.css
   branches/djamnedlies/stats/data/star.png   (contents, props changed)
   branches/djamnedlies/stats/data/style.css
   branches/djamnedlies/stats/data/t.png   (contents, props changed)
   branches/djamnedlies/stats/data/tab_left.png   (contents, props changed)
   branches/djamnedlies/stats/data/tab_right.png   (contents, props changed)
   branches/djamnedlies/stats/data/warn.png   (contents, props changed)
   branches/djamnedlies/stats/data/webpage.png   (contents, props changed)
   branches/djamnedlies/stats/defaults.py
   branches/djamnedlies/stats/management/
   branches/djamnedlies/stats/management/__init__.py
   branches/djamnedlies/stats/management/commands/
   branches/djamnedlies/stats/management/commands/__init__.py
   branches/djamnedlies/stats/management/commands/migrate.py
   branches/djamnedlies/stats/models.py
   branches/djamnedlies/stats/templates/
   branches/djamnedlies/stats/templates/base.tmpl
   branches/djamnedlies/stats/templates/index.tmpl
   branches/djamnedlies/stats/templates/language-release-stats.tmpl
   branches/djamnedlies/stats/templates/language-release.tmpl
   branches/djamnedlies/stats/templates/list-languages.tmpl
   branches/djamnedlies/stats/templates/list-modules.tmpl
   branches/djamnedlies/stats/templates/list-releases.tmpl
   branches/djamnedlies/stats/templates/list-teams.tmpl
   branches/djamnedlies/stats/templates/module-images.tmpl
   branches/djamnedlies/stats/templates/module.tmpl
   branches/djamnedlies/stats/templates/people.tmpl
   branches/djamnedlies/stats/templates/person-base.tmpl
   branches/djamnedlies/stats/templates/person.tmpl
   branches/djamnedlies/stats/templates/release.tmpl
   branches/djamnedlies/stats/templates/show-stats.tmpl
   branches/djamnedlies/stats/templates/team.tmpl
   branches/djamnedlies/stats/urls.py
   branches/djamnedlies/stats/utils.py
   branches/djamnedlies/stats/views.py
   branches/djamnedlies/urls.py

Added: branches/djamnedlies/README
==============================================================================
--- (empty file)
+++ branches/djamnedlies/README	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,12 @@
+This is a re-implementation of the Damned-Lies application
+in a Django app. Damned-Lies has been originally written by
+Danilo Segan (danilo gnome org). This implementation has been
+written by Claude Paroz (claude 2xlibre net).
+
+The former XML files (modules, releases, people, translation
+teams) have been replaced by a database.
+
+The Data model is in the /docs directory.
+
+The middle-term objective is to merge this code with the Transifex 
+application (http://transifex.org).

Added: branches/djamnedlies/TODO
==============================================================================
--- (empty file)
+++ branches/djamnedlies/TODO	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,4 @@
+Things left to do:
+ * i18n (translation for strings in database?)
+ * replace entirely defaults.py by conf/settings.py
+ * implement update-stats.py script

Added: branches/djamnedlies/__init__.py
==============================================================================

Added: branches/djamnedlies/docs/DataModel.odg
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/manage.py
==============================================================================
--- (empty file)
+++ branches/djamnedlies/manage.py	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+from django.core.management import execute_manager
+try:
+    import settings # Assumed to be in the same directory.
+except ImportError:
+    import sys
+    sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
+    sys.exit(1)
+
+if __name__ == "__main__":
+    execute_manager(settings)

Added: branches/djamnedlies/settings.py
==============================================================================
--- (empty file)
+++ branches/djamnedlies/settings.py	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,83 @@
+# Django settings for djamnedlies project.
+
+import os
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+PROJECT_PATH = os.path.dirname(os.path.abspath(__file__))
+
+ADMINS = (
+    ('Claude Paroz', 'claude 2xlibre net'),
+)
+
+MANAGERS = ADMINS
+
+DATABASE_ENGINE = 'sqlite3'           # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+DATABASE_NAME = os.path.join(PROJECT_PATH,'database.db')            # Or path to database file if using sqlite3.
+DATABASE_USER = ''             # Not used with sqlite3.
+DATABASE_PASSWORD = ''         # Not used with sqlite3.
+DATABASE_HOST = ''             # Set to empty string for localhost. Not used with sqlite3.
+DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'Europe/Zurich'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en-US'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# Absolute path to the directory that holds media.
+# Example: "/home/media/media.lawrence.com/"
+MEDIA_ROOT = ''
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash if there is a path component (optional in other cases).
+# Examples: "http://media.lawrence.com";, "http://example.com/media/";
+MEDIA_URL = ''
+
+# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
+# trailing slash.
+# Examples: "http://foo.com/media/";, "/media/".
+ADMIN_MEDIA_PREFIX = '/media/'
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = 'zk!^92901p458c8lo0(fox-&k7jj(aple76_k%eva7b1)xjo8-'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.load_template_source',
+    'django.template.loaders.app_directories.load_template_source',
+#     'django.template.loaders.eggs.load_template_source',
+)
+
+MIDDLEWARE_CLASSES = (
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+)
+
+ROOT_URLCONF = 'djamnedlies.urls'
+
+TEMPLATE_DIRS = (
+    os.path.join(PROJECT_PATH, 'stats', 'templates'),
+)
+
+INSTALLED_APPS = (
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.sites',
+    'django.contrib.admin',
+    'stats',
+)

Added: branches/djamnedlies/stats/__init__.py
==============================================================================

Added: branches/djamnedlies/stats/admin.py
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/admin.py	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2008 Claude Paroz <claude 2xlibre net>.
+#
+# This file is part of Damned Lies.
+#
+# Damned Lies is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# Damned Lies is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Damned Lies; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from djamnedlies.stats.models import Statistics
+from django.contrib import admin
+
+admin.site.register(Statistics)
+

Added: branches/djamnedlies/stats/conf/__init__.py
==============================================================================

Added: branches/djamnedlies/stats/conf/settings.py
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/conf/settings.py	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,5 @@
+from django.conf import settings
+
+WEBROOT = "/stats"
+POTDIR = "/home/claude/www/damned-lies/cvs/POT"
+SCRATCHDIR = "/home/claude/www/damned-lies/cvs/"

Added: branches/djamnedlies/stats/data/bar.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/cyan-bar.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/download.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/emptyimg.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/error.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/figure.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/foot-16.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/foot.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/general_bg.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/general_separator.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/gnome-64.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/gnome-gtp.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/green-bar.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/info.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/layout.css
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/data/layout.css	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,442 @@
+/* Basic tags */
+body {
+	margin: 0px;
+	background-color: white;
+	font-family: sans-serif;
+	color: black;
+}
+
+#body {
+	margin: 90px 230px 0px 10px;
+	padding: 0px;
+}
+
+a img {
+	border: 0px;
+}
+
+/* Anchors */
+a {
+	color: #0000ff;
+}
+
+a:visited {
+	color: #551a8b;
+}
+
+a:active {
+	color: #ff0000;
+}
+
+/* Basic classes */
+
+.none { /* to add paragraph spacing to various elements for ttys */
+	margin: 0px;
+	padding: 0px;
+}
+
+.invisible { /* stuff that should appear when this css isn't used */
+	margin: 0px;
+	border: 0px;
+	padding: 0px;
+	height: 0px;
+	visibility: hidden;
+}
+
+.left {
+	margin: 10px;
+	padding: 0px;
+	float: left;
+}
+
+.right {
+	margin: 10px;
+	padding: 0px;
+	float: right;
+}
+
+.center {
+	text-align: center;
+}
+
+/* Common page elements: Header, footer, etc. */
+
+#logo {
+	position: absolute;
+	top: 10px;
+	left: 10px;
+	border: 0px;
+	z-index: 10;
+
+	width: 64px;
+	height: 64px;
+}
+
+#logo a img {
+	width: 64px;
+	height: 64px;
+}
+
+#hdr {
+	position: absolute;
+	z-index: 5;
+
+	top: 0px;
+	left: 0px;
+	right: 0px;
+	width: 100%;
+	height: 48px;
+
+	text-align: right;
+
+	background-color: #e3ffc3;
+	border-bottom: 1px solid #807d74;
+}
+
+#banner {
+	position: absolute;
+	z-index: 10;
+
+	top: 0px;
+	right: 0px;
+	border: 0px;
+
+	width: 300px;
+	height: 48px;
+
+}
+
+#banner a img {
+	width: 300px;
+	height: 48px;
+}
+
+#hdrNav {
+	position: absolute;
+	top: 54px;
+	left: 0px;
+
+	margin-left: 84px;
+	text-align: left;
+	vertical-align: middle;
+	font-size: small;
+}
+
+#hdrNav a {
+	color: #000000;
+}
+
+#hdrTitle {
+	height: 48px;
+	padding: 10px 10px 0px 0px;
+	font-weight: bold;
+}
+
+
+/* Search thingy */
+
+#search {
+	font-size: small;
+	margin-bottom: 10px;
+	padding: 10px;
+	background-color: #dddddd;
+}
+
+#search input {
+	border: 1px solid #666666;
+	background-color: #ffffff;
+
+	vertical-align: middle;
+}
+
+
+/* Sidebar */
+
+#sidebar {
+	position: absolute;
+	top: 90px;
+	right: 0px;
+	width: 210px;
+
+	/*margin-right: 10px;*/
+	padding-right: 10px;
+	padding-bottom: 0px;
+	border-left: 1px dashed #dddddd;
+	background-color: #ffffff;
+}
+
+#sidebar p {
+	margin-top: 0px;
+	padding-left: 10px;
+	padding-right: 10px;
+}
+
+#sidebar p.section {
+	text-align: center;
+	font-weight: bold;
+	padding-top: 3px;
+	padding-bottom: 3px;
+	color: #999999;
+	background-color: #eeeeee;
+}
+
+#sidebar ul {
+  margin: 0em;
+  margin-bottom: 15px;
+  padding-left: 10px;
+  padding-right: 10px;
+  list-style-type: none;
+}
+
+#sidebar ul ul {
+  padding-left: 2em;
+  padding-right: 0em;
+  list-style-type: square;
+}
+
+
+/* Copyright footer */
+
+#copyright {
+	text-align: center;
+	font-size: small;
+	clear: both;
+
+	margin-top: 10px;
+	padding: 5px 0px 5px 0px;
+	color: #aaaaaa;
+}
+
+#copyright a {
+	color: #aaaaff;
+}
+
+#copyright a:visited {
+	color: #ffaaaa;
+}
+
+
+/* News Sections */
+
+p.newsitem {
+	clear: left;
+	margin-bottom: 20px;
+}
+
+p.newsitem img.newsicon {
+	float: left;
+	margin: 0px 10px 10px 10px;
+	border: 0px;
+}
+
+
+/* Generic Classes */
+
+div.code {
+	background-color: #e0e0e0;
+	color: #000000;
+	white-space: pre;
+	font-family: monospace;
+}
+
+
+body {
+	margin: 0px;
+	padding: 0px;
+	font-family: sans-serif;
+	background: white url(star.png) -100px -200px no-repeat;
+	height: 101%;
+}
+
+#page {
+	margin: 0px;
+	padding: 0px;
+}
+
+div.in-column {
+	margin: 0 0 2em 1em;
+	float: right;
+	max-width: 12em;
+}
+
+hr {
+	color: #888;
+	background: #888;
+	border: 0;
+	height: 1px;
+	width: 90%;
+	text-align: center;
+	clear: both;
+}
+
+
+div.body {
+	clear: both;
+}
+
+
+div.sidebar {
+	position: absolute;
+	text-align: left;
+	right: 0px;
+	top: 60px;
+	width: 27ex;
+	padding-left: 1ex;
+	border-left: 1ex solid #eee;
+	margin-top: 4em;
+}
+
+div.sidebar h2 {
+	margin-top: 0;
+	padding: 5px 2ex 5px 2ex;
+	background: url(t.png) top left repeat-y;
+	font-size: 100%;
+}
+
+ul.toc {
+	padding: 0;
+	padding-left: 20px;
+	margin-left: 0;
+	margin-right: 10px;
+	list-style: none;
+}
+
+ul.toc li {
+	list-style: circle;
+}
+
+ul.toc li a {
+	text-decoration: none;
+	color: black;
+}
+
+ul.toc li a:hover {
+	text-decoration: underline;
+}
+
+#general {
+	list-style: none;
+	background: #2E3436 url(general_bg.png) 0 100% repeat-x;
+	text-align: right;
+	padding: 0 1ex;
+	margin: 0;
+	font-size: 70%;
+}
+
+#general li {
+	display: inline;
+	background: url(general_separator.png) 0 0 no-repeat;
+	padding-top: 10px;
+	padding-bottom: 8px;
+	margin-left: 0px;
+	margin-top: 0px;
+}
+
+#general li a {
+	font-weight: bold;
+	color: #FFFFFF;
+	margin: 0 2ex;
+	text-decoration: none;
+	line-height: 30px;
+}
+
+#general li a:hover {
+	text-decoration: underline;
+}
+
+#general .home {
+	float: left;
+	background: url(general_separator.png) 100% 0 no-repeat;
+	padding-top: 0;
+	padding-bottom: 0;
+}
+
+#general .home a {
+	float: left;
+	background: url(foot.png) 7px 50% no-repeat;
+	margin-left: 0;
+	padding-left: 27px;
+}
+
+
+#header {
+	background: #729FCF url(gnome-gtp.png) 15px 10px no-repeat;
+	float: left;
+	width: 100%;
+	font-size: 75%;
+}
+
+#header h1 {
+	margin: 0;
+	margin-left: 85px;
+	padding-top: 25px;
+	font-size: 200%;
+	color: #eeeeec;
+}
+
+#tabs {
+	background: url(bar.png) 0 100% repeat-x;
+	width: 100%;
+	float: left;
+	margin: 0;
+	padding: 0;
+}
+
+#portal-globalnav {
+	float: right;
+	list-style: none;
+	margin: 0;
+	margin-right: 3ex;
+}
+
+#portal-globalnav li {
+	float: left;
+	margin: 0;
+	margin-left: 0.2ex;
+	font-size: 2ex;
+}
+
+#portal-globalnav li a:hover {
+	color: #111111;
+}
+
+#portal-globalnav li a {
+	float: left;
+	text-decoration: none;
+	color: #555555;
+	background: url(tab_left.png) 0 0 no-repeat;
+	padding: 7px 0 7px 7px;
+	border-bottom: 2px solid #CCCCCC;
+}
+
+#portal-globalnav li span {
+	background: url(tab_right.png) 100% 0 no-repeat;
+	padding: 7px 28px 7px 19px;
+}
+
+#portal-globalnav li.selected a {
+	color: #3566A5;
+	background: url(tab_left.png) 0 -57px no-repeat;
+	border-bottom: none;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+#portal-globalnav li.selected a span {
+	background: url(tab_right.png) 100% -57px no-repeat;
+	padding-top: 8px;
+	padding-bottom: 8px;
+}
+
+/* page content */
+
+div#content {
+	clear: both;
+	padding: 1em;
+	margin: 1em;
+}
+
+/*show a foot logo instead of dots in some lists*/
+ul.foot li {
+   list-style-image: url('foot-16.png');
+}

Added: branches/djamnedlies/stats/data/main.css
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/data/main.css	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,146 @@
+.stats th { 
+    text-align: left; 
+    background: gray; 
+    color: white;
+    padding-left: 2px;
+    padding-right: 2px; 
+}
+
+.stats td { 
+    background: #f0f0f0; 
+    text-align: center;
+    padding-left: 2px;
+    padding-right: 2px;
+    color: black;
+}
+
+.stats td.leftcell { 
+    text-align: left;
+}
+
+h2 {
+    width: 100%;
+    margin-top: 2em;
+    border-bottom: 1px solid gray;
+}
+
+div.graph {
+  height: 14px;
+  width: 100px;
+  border: 0px solid black; 
+  position: relative;
+}
+
+div.translated {
+  position: absolute; 
+  top: 0px;
+  height:100%; 
+  left: 0%; 
+  background: #448844;
+  background: url('green-bar.png');
+  background-repeat: repeat-x;
+  color: black;
+}
+
+div.goodchange {
+  position: absolute; 
+  top: 0px;
+  height:100%; 
+  background: #55DD55;
+  background: url('cyan-bar.png');
+  background-repeat: repeat-x;
+  color: black;
+}
+
+div.fuzzy {
+  position: absolute; 
+  top: 0px;
+  height:100%; 
+  background: #4444AA;
+  background: url('purple-bar.png');
+  background-repeat: repeat-x;
+  color: black;
+}
+
+div.untranslated {
+  position: absolute; 
+  height:100%; 
+  top: 0px;
+  background: #FF4444;
+  background: url('red-bar.png');
+  background-repeat: repeat-x;
+  color: black;
+}
+
+a {
+  text-decoration: none;
+  color: #406080;
+}
+
+a:hover, a:visited {
+  color: #6080a0;
+}
+
+a:hover {
+  text-decoration: underline;
+}
+
+
+table {
+  width: 100%;
+}
+
+td.fuzzy {
+  background: #c98e7f;
+}
+
+img {
+  border: 0px;
+}
+
+img.people {
+  float: left;
+  margin-left: -85px;
+}
+
+img.screenshot {
+  max-width: 550px;
+  border: 0;
+}
+
+div.maintainer {
+  padding-left: 85px;
+  margin-bottom: 12px;
+  clear: both;
+}
+
+div.mainpage {
+  width:80%;
+  text-align: left;
+  margin-right: auto;
+  margin-left: auto;
+}
+
+div.col1 { float: left }
+div.col2 { float: left }
+div.col3 { float: left }
+
+p#show, p#hide {
+  text-align: right;
+  font-style: italic;
+}
+
+.path {
+  font-size: x-small;
+  font-style: italic;
+  color: #666666;
+}
+
+.footnote {
+  text-align: center;
+  font-size: small;
+  clear: both;
+  margin-top: 20px;
+  color: #aaaaaa;
+}
+

Added: branches/djamnedlies/stats/data/main.js
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/data/main.js	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,30 @@
+// ***
+// This function shows or hides all modules in a release that are
+// 100% translated
+// ***
+function showHideCompleted() {
+    var regex = /complete$/i;
+
+    var tbls = document.getElementsByName("stats-table");
+    for (var tb=0; tb < tbls.length; tb++) {
+        var translations = tbls[tb].getElementsByTagName("tr");
+
+        for(var i=0;i<translations.length;i++) {
+            if ( regex.exec(translations[i].id) ) {
+                if ( translations[i].style.display != 'none' ) {
+                    translations[i].style.display = 'none';
+                }
+                else {
+                    translations[i].style.display = '';
+                }
+            }
+        }
+    }
+    
+    var hide = document.getElementById("hide");
+    var show = document.getElementById("show");
+
+    hide.style.display = (hide.style.display != 'none' ? 'none' : '' );
+    show.style.display = (show.style.display != 'none' ? 'none' : '' );
+    return false;
+}

Added: branches/djamnedlies/stats/data/nobody.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/purple-bar.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/red-bar.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/rtl.css
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/data/rtl.css	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,30 @@
+.stats th { 
+    text-align: right; 
+    padding-right: 2px;
+    padding-left: 2px; 
+}
+
+.stats td { 
+    padding-right: 2px;
+    padding-left: 2px;
+}
+
+.stats td.left { 
+    text-align: right;
+}
+
+div.translated {
+  right: 0%; 
+}
+
+div.maintainer {
+  padding-right: 85px; 
+  background-position: top right; 
+}
+
+div.mainpage {
+  width:80%;
+  text-align: right;
+  margin-left: auto;
+  margin-right: auto;
+}

Added: branches/djamnedlies/stats/data/star.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/style.css
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/data/style.css	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,151 @@
+/**
+ * Styles that are not a part of page layout.
+ *
+ * For example:
+ *  Fonts
+ *  Sizes
+ *  Decoration
+ *  Separators
+ */
+body * {
+  font-family: verdana, arial, sans-serif;
+}
+
+div#content a {
+	color: #3465a4;
+	border-bottom: 1px dotted #888;
+	text-decoration: none;
+}
+
+div#content a:hover {
+	border-bottom: 1px solid #888;
+}
+
+
+h1.first {
+  margin-top: 0;
+  padding-top: 0;
+}
+
+h2, h3, h4, h5, h6 {
+  color: #3f3f3f;
+}
+
+h1 {
+  font-size: 1.4em;
+}
+
+h2 {
+  font-size: 1.2em;
+}
+
+h3 {
+  font-size: 1.0em;
+}
+
+/*  lists  */
+.list {
+  margin-top:.5em;
+}
+.list tr td {
+  padding:.2em;
+  text-align:left;
+}
+.list td label {
+  border-bottom:1px dashed #999;
+  font-weight:normal;
+}
+.list th {
+  background: #ccf;
+  border: 1px solid #000;
+  font-weight: bold;
+  padding: 2px;
+}
+.list th a {
+  display: block;
+  padding:.2em 1.2em .2em .2em;
+  text-align: left;
+}
+.list th a:hover {
+  background-color: #fff;
+}
+.row1 {
+  background-color: #eee;
+}
+.row2 {
+  background-color: #ddd;
+}
+.row1:hover, .row2:hover {
+  background-color: #fff;
+}
+
+.record th {
+  text-align: right;
+}
+
+/* styling page content */
+
+h1 {
+	font-size: 1.5em;
+	color: #3f3f3f;
+}
+
+/* styling form widgets like bugzilla.gnome.org */
+input,textarea {
+ border: 1px solid #6f6f6f;
+/* background: #dddddd; */
+}
+
+input.login_small {
+ border-style: none;
+}
+
+input:focus,textarea:focus {
+  background-color: #f7f2d0;
+  color: #000000;
+}
+
+/* select {
+ border: groove
+} */
+
+option {
+ border: 0px none #ffffff;
+}
+
+input[type=radio] {
+  margin-left: 1em;
+}
+
+/* footer */
+
+#footer {
+	text-align: center;
+	margin: 3em 3em 1em 3em;
+	border-top: 1px solid gray;
+	padding-top: 1.5em;
+	color: #888;
+	font-size: 80%;
+	clear: both;
+}
+
+#footer ul {
+	margin: 0;
+	padding: 0;
+}
+
+#footer li {
+	display: inline;
+	padding: 0 1em;
+}
+
+#footer a {
+	color: #3465a4;
+	border-bottom: 1px dotted #888;
+	text-decoration: none;
+}
+
+#footer a:hover {
+	border-bottom: 1px solid #888
+}
+

Added: branches/djamnedlies/stats/data/t.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/tab_left.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/tab_right.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/warn.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/data/webpage.png
==============================================================================
Binary file. No diff available.

Added: branches/djamnedlies/stats/defaults.py
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/defaults.py	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+
+# Directory to checkout source code and dump dead bodies to
+scratchdir = u"/home/danilo/cvs/gnom"
+
+# Web root directory
+webroot = ''
+
+# whether to generate translated XML documentation (might slow down the process significantly)
+# they'll be put into os.path.join(scratchdir,"xml")
+generate_docs = 0
+
+WHEREAREWE = 'http://i18n-status.gnome.org/'
+WHOAREWE = 'danilo gnome org'
+
+# When in STRINGFREEZE, where to send notifications (gnome-i18n gnome org) on any POT changes
+notifications_to = 'gnome-i18n gnome org'
+notifications_to = 'danilo smorisa kvota net'
+
+# Whether to use fuzzy matching (much slower, but better for translators), use 0 only when testing!
+fuzzy_matching = 0
+
+# Set DEBUG to 1 to print (too) many messages on stderr about progress
+DEBUG = 1
+
+# WARNING:
+#   You usually don't want to set most of the things below,
+#   unless you are using Damned Lies for something other than GNOME
+
+
+# Directory to hold resulting POT/PO files
+import os.path
+potdir = os.path.join(scratchdir, "POT")
+
+# default to Gnome Bugzilla, product same as module ID and component "general"
+bugzilla = {
+    "baseurl" : u"http://bugzilla.gnome.org/";,
+    "xmlrpc" : u"http://bugzilla-test.gnome.org/xmlrpc.cgi";,
+    "product" : u"",
+    "component" : u"general",
+    }
+
+# default to a single "po" directory containing UI translations, and module ID for POT name
+translation_domains = {
+    u"po" : {"description" : u"UI translations", "potbase": ""} # description, base potname
+    }
+
+# default to a single "help" document containing User Guide
+documents = {
+    u"help" : {"description" : u"User Guide", "potbase" : "" }
+    }
+
+# default to anonyomus Gnome CVS
+cvsroot = u":pserver:anonymous anoncvs gnome org:/cvs/gnome"
+cvsweb = u"http://cvs.gnome.org/viewcvs/%(module)s?only_with_tag=%(branch)s"
+cvsbranch = {
+    u"HEAD": { "translation_domains": translation_domains,
+               "documents": documents,
+               }
+    }
+
+# Default language to fallback to
+language = 'en'
+
+# Right-to-left languages
+rtl_languages = [ 'ar', 'fa', 'he', 'urd', 'yi']
+
+# Dummy function for deferred translation (error messages)
+# See http://docs.python.org/lib/node742.html
+def N_(message): return message.replace("%s","###%s###")

Added: branches/djamnedlies/stats/management/__init__.py
==============================================================================

Added: branches/djamnedlies/stats/management/commands/__init__.py
==============================================================================

Added: branches/djamnedlies/stats/management/commands/migrate.py
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/management/commands/migrate.py	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,193 @@
+from django.core.management.base import BaseCommand
+import os
+import data
+from djamnedlies.stats.models import Person, Team, Language, Module, Branch, Domain, Release, Category, Statistics
+
+class Command(BaseCommand):
+    """ Before the migration:
+        1. cp or link the legacy D-L data.py file in this directory
+        2. set the xml_base directory where you can find the original .xml files to migrate their content """
+    
+    help = "Migrate current D-L XML files into database content"
+
+    output_transaction = False
+    xml_base = ""
+
+    def handle(self, app, **options):
+        #drop table language;drop table module;drop table module_maintainer;drop table person;drop table release;drop table category;drop table team;drop table branch;
+        
+        print self.migratePeople()
+        print self.migrateTeams()
+        print self.migrateModules()
+        print self.migrateReleases()
+        print self.migrateStats()
+       
+        return "Migration completed."
+
+    def migratePeople(self):
+        people = data.readFromFile(os.path.join(self.xml_base, "people.xml"))
+        for key, p in people.items():
+            if not p.has_key('bugzilla-account'):
+                p['bugzilla-account'] = None
+            if p['id'][:2] != "x-":
+                p['svn-account'] = p['id']
+            else:
+                p['svn-account'] = None
+            new_p = Person(old_id=p['id'], name=p['name'], 
+                           email=p['email'], svn_account=p['svn-account'], image=p['icon'], 
+                           webpage_url=p['webpage'], irc_nick=p['nick'], 
+                           bugzilla_account=p['bugzilla-account'])
+            new_p.save()
+        return "People migrated successfully"
+
+    def migrateTeams(self):
+        teams = data.readFromFile(os.path.join(self.xml_base, "translation-teams.xml"))
+        for key, team in teams.items():
+            if len(team['language'].items()) <= 1:
+                team['description'] = team['language'].items()[0][1]['content']
+            coord = Person.objects.get(old_id=team['coordinator'].keys()[0])
+            if not team.has_key('mailing-list'):
+                team['mailing-list'] = None
+            if not team.has_key('mailing-list-subscribe'):
+                team['mailing-list-subscribe'] = None
+            if not team.has_key('description'):
+                team['description'] = 'Catalan'
+                print "Forced Catalan description"
+            new_t = Team(lang_code=key, description=team['description'], coordinator=coord,
+                         webpage_url=team['webpage'],
+                         mailing_list=team['mailing-list'],
+                         mailing_list_subscribe=team['mailing-list-subscribe'])
+            new_t.save()
+            for lkey, lang in team['language'].items():
+                new_l = Language(name=lang['content'], 
+                                 locale=lang['id'], team=new_t)
+                new_l.save()
+        return "Teams migrated successfully"
+
+    def migrateModules(self):
+        modules = data.readFromFile(os.path.join(self.xml_base, "gnome-modules.xml"))
+        for key, module in modules.items():
+            for prop in ['webpage', 'comment']:
+                if not module.has_key(prop):
+                    module[prop] = None
+            new_m = Module(name=module['id'], description=module['description'], 
+                           homepage=module['webpage'], comment=module['comment'],
+                           bugs_base=module['bugs-baseurl'], bugs_product=module['bugs-product'], bugs_component=module['bugs-component'],
+                           vcs_type=module['scmroot']['type'], vcs_root=module['scmroot']['path'], vcs_web=module['scmweb'])
+            new_m.save()
+            # Adding maintainers
+            if module.has_key('maintainer'):
+                for m in module['maintainer'].items():
+                    person = Person.objects.get(old_id=m[0])
+                    new_m.maintainers.add(person)
+            # Adding branches
+            for bkey, bval in module['branch'].items():
+                new_b = Branch(name=bkey, module=new_m)
+                new_b.save()
+                # Adding domains (to module), if not exist
+                for dkey, dval in bval['domain'].items():
+                    if dval.has_key('directory'):
+                        ddir = dval['directory']
+                    else:
+                        ddir = 'po'
+                    existing_d = Domain.objects.filter(module=new_m, dtype='ui', directory=ddir)
+                    if len(existing_d) < 1:
+                        if not dval.has_key('description'):
+                            dval['description'] = None
+                        new_domain = Domain(module=new_m, name=dkey, description=dval['description'], dtype='ui', directory=ddir)
+                        new_domain.save()
+                    #else:
+                for dkey, dval in bval['document'].items():
+                    if dval.has_key('directory'):
+                        ddir = dval['directory']
+                    else:
+                        ddir = 'help'
+                    existing_d = Domain.objects.filter(module=new_m, dtype='doc', directory=ddir)
+                    if len(existing_d) < 1:
+                        if not dval.has_key('description'):
+                            dval['description'] = None
+                        new_domain = Domain(module=new_m, name=dkey, description=dval['description'], dtype='doc', directory=ddir)
+                        new_domain.save()
+                    
+        return "Modules migrated successfully"
+
+    def migrateReleases(self):
+        releases = data.readFromFile(os.path.join(self.xml_base, "releases.xml"))
+        for key, release in releases.items():
+            try:
+                new_r = Release.objects.get(name=release['description'])
+            except:
+                new_r = Release(name=release['description'], stringfrozen=False, status=release['status'])
+                new_r.save()
+            if release.has_key('category'):
+                for catname, catcontent in release['category'].items():
+                    relcat = Category(release=new_r, description=catcontent['description'])
+                    relcat.save()
+                    for mod, content in catcontent['module'].items():
+                        # find the right component
+                        module = Module.objects.get(name=mod)
+                        if content.has_key('branch'):
+                            branch_name = content['branch']
+                        else:
+                            if module.vcs_type == 'git':
+                                branch_name = u'master'
+                            else:
+                                branch_name = u'HEAD'
+                        # set the release field of the branch (with relcat)
+                        for br in module.branch_set.all():
+                            if br.name == branch_name:
+                                br.category = relcat
+                                br.save()
+            else:
+                relcat = Category(release=new_r, description='default')
+                relcat.save()
+                for mod, content in release['module'].items():
+                    module = Module.objects.get(name=mod)
+                    if content.has_key('branch'):
+                        branch_name = content['branch']
+                    else:
+                        if module.vcs_type == 'git':
+                            branch_name = u'master'
+                        else:
+                            branch_name = u'HEAD'
+                    # set the release field of the branch (with relcat)
+                    for br in module.branch_set.all():
+                        if br.name == branch_name:
+                            br.category = relcat
+                            br.save()
+        return "Releases migrated successfully"
+
+    def migrateStats(self):
+        stats = Statistics.objects.all()
+        for stat in stats:
+            # link to Branch, Domain and Language
+            try:
+                mod = Module.objects.get(name=stat.Module)
+            except:
+                print "Unable to find module corresponding to '%s'." % stat.Module
+                continue
+            try:
+                br = Branch.objects.get(name=stat.Branch, module=mod.id)
+            except:
+                print "Unable to find branch corresponding to '%s.%s'." % (stat.Module, stat.Branch)
+                continue
+            if stat.Language is not None:
+                try:
+                    lang = Language.objects.get(locale=stat.Language)
+                except:
+                    lang = Language(name=stat.Language, 
+                                 locale=stat.Language)
+                    lang.save()
+                    print "Unable to find language corresponding to '%s'. Language created." % (stat.Language)
+            else:
+                # The POT file
+                lang = None
+            stat.branch = br
+            stat.language = lang
+            for p in mod.domain_set.all():
+                if p.dtype == stat.Type and p.name == stat.Domain:
+                    stat.domain = p
+                    break
+            #import pdb; pdb.set_trace()
+            stat.save()
+        return "Statistics migrated successfully"

Added: branches/djamnedlies/stats/models.py
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/models.py	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,647 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2008 Claude Paroz <claude 2xlibre net>.
+#
+# This file is part of Damned Lies.
+#
+# Damned Lies is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# Damned Lies is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Damned Lies; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from django.db import models, connection
+from django.utils.translation import ungettext, ugettext as _
+from stats.conf import settings
+from stats import utils
+from time import tzname
+import os, re, commands
+
+class Person(models.Model):
+    old_id = models.CharField(max_length=50)
+    name = models.CharField(max_length=50)
+    email = models.CharField(max_length=50)
+    svn_account = models.CharField(max_length=20, null=True)
+    image = models.CharField(max_length=50, null=True)
+    webpage_url = models.CharField(max_length=50, null=True)
+    irc_nick = models.CharField(max_length=20, null=True)
+    bugzilla_account = models.CharField(max_length=50, null=True)
+    class Meta:
+        db_table = 'person'
+
+    def nospamemail(self):
+        return utils.obfuscateEmail(self.email)
+    def nospambugzillaaccount(self):
+        return utils.obfuscateEmail(self.bugzilla_account)
+
+class Language(models.Model):
+    name = models.CharField(max_length=50)
+    locale = models.CharField(max_length=15)
+    team = models.ForeignKey('Team', null=True)
+    class Meta:
+        db_table = 'language'
+    
+    def bugs_url_enter(self):
+        return "http://bugzilla.gnome.org/enter_bug.cgi?product=l10n&amp;component=%s%%20[%s]"; %  (self.name, self.locale)
+        
+    def bugs_url_show(self):
+        return "http://bugzilla.gnome.org/buglist.cgi?product=l10n&amp;component=%s%%20[%s]&amp;bug_status=NEW&amp;bug_status=REOPENED&amp;bug_status=ASSIGNED&amp;bug_status=UNCONFIRMED"; % (self.name, self.locale)          
+
+    def get_release_stats(self):
+        """ Get summary stats for all releases """
+        releases = Release.objects.all().order_by('status', '-name')
+        stats = []
+        for rel in releases:
+            stats.append(rel.total_for_lang(self))
+        return stats
+
+class Team(models.Model):
+    lang_code = models.CharField(max_length=15, unique=True)
+    description = models.TextField()
+    coordinator = models.ForeignKey('Person')
+    webpage_url = models.CharField(max_length=50, null=True)
+    mailing_list = models.CharField(max_length=50, null=True)
+    mailing_list_subscribe = models.CharField(max_length=50, null=True)
+
+    class Meta:
+        db_table = 'team'
+
+    def __unicode__(self):
+        return self.description
+    
+class Module(models.Model):
+    name = models.CharField(max_length=50)
+    homepage = models.CharField(max_length=50, null=True)
+    description = models.TextField(null=True)
+    comment = models.TextField(null=True)
+    bugs_base = models.CharField(max_length=50)
+    bugs_product = models.CharField(max_length=50)
+    bugs_component = models.CharField(max_length=50)
+    vcs_type = models.CharField(max_length=5, choices=(('cvs', 'CVS'), 
+                                                       ('svn', 'Subversion'), 
+                                                       ('git', 'Git'),
+                                                       ('hg', 'Mercurial'),
+                                                       ('bzr', 'Bazaar')))
+    vcs_root = models.CharField(max_length=50)
+    vcs_web = models.CharField(max_length=50)
+    
+    maintainers = models.ManyToManyField(Person, db_table='module_maintainer')
+    class Meta:
+        db_table = 'module'
+    
+    def get_bugs_i18nurl(self):
+        if self.bugs_base.find("bugzilla") != -1 or self.bugs_base.find("freedesktop") != -1:
+            return "%sbuglist.cgi?product=%s&amp;component=%s&amp;keywords_type=anywords&amp;keywords=I18N+L10N&amp;bug_status=UNCONFIRMED&amp;bug_status=NEW&amp;bug_status=ASSIGNED&amp;bug_status=REOPENED&amp;bug_status=NEEDINFO" % (self.bugs_base, self.bugs_product, self.bugs_component)
+        else:
+            return None
+
+    def get_bugs_enterurl(self):
+        if self.bugs_base.find("bugzilla") != -1 or self.bugs_base.find("freedesktop") != -1:
+            return "%senter_bug.cgi?product=%s&amp;component=%s" % (self.bugs_base, self.bugs_product, self.bugs_component)
+        else:
+            return self.bugs_base
+    
+    def compare_branches(self, a, b):
+        if a.name in ('HEAD', 'master'):
+            return -1
+        elif b.name in ('HEAD', 'master'):
+            return 1
+        else:
+            return cmp(a.name, b.name)*-1    
+    
+    def get_branches(self):
+        branches = [b for b in self.branch_set.all()]
+        branches.sort(self.compare_branches)
+        return branches
+
+class Branch(models.Model):
+    """ Branch of a module """
+    name = models.CharField(max_length=50)
+    #description = models.TextField(null=True)
+    module = models.ForeignKey(Module)
+    category = models.ForeignKey('Category', null=True)
+    class Meta:
+        db_table = 'branch'
+    
+    def is_head(self):
+        if self.module.vcs_type in ('cvs', 'svn') and self.name == "HEAD":
+            return True
+        elif self.module.vcs_type == 'git' and self.name == "master":
+            return True
+        return False
+    
+    def get_vcsurl(self):
+        if self.is_head():
+            return "%s/%s/trunk" % (self.module.vcs_root, self.module.name)
+        else:
+            return "%s/%s/branches/%s" % (self.module.vcs_root, self.module.name, self.name)
+
+    def get_vcsweburl(self):
+        if self.is_head():
+            return "%s/trunk" % (self.module.vcs_web)
+        else:
+            return "%s/branches/%s" % (self.module.vcs_web, self.name)
+
+    def co_path(self):
+        """ Returns the path of the local checkout for the branch """
+        return os.path.join(settings.SCRATCHDIR, self.module.vcs_type, self.module.name + "." + self.name)
+    
+    def get_stats(self, typ):
+        """ Get statistics list of type typ ('ui' or 'doc'), in a dict of lists, key is domain.name (POT in 1st position)"""
+        stats = {}
+        for stat in self.statistics_set.order_by('Translated').reverse():
+            if stat.domain.dtype == typ:
+                if stats.has_key(stat.domain.name):
+                    if stat.Language:
+                        stats[stat.domain.name].append(stat)
+                    else:
+                        stats[stat.domain.name].insert(0, stat) # This is the POT file
+                else:
+                    stats[stat.domain.name] = [stat,]
+        # sort by domain, then translated
+        #templist = [(obj_.domain_id, obj_.Translated, obj_) for obj_ in stats]
+        #templist.sort()
+        #return [obj_ for (key1, key2, obj_) in templist]
+        return stats
+    
+    def get_docstats(self):
+        return self.get_stats('doc')
+    def get_uistats(self):
+        return self.get_stats('ui')
+
+class Domain(models.Model):
+    module = models.ForeignKey(Module)
+    name = models.CharField(max_length=50)
+    description = models.TextField(null=True)
+    dtype = models.CharField(max_length=5, choices=(('ui', 'User Interface'), ('doc', 'Documentation')))
+    directory = models.CharField(max_length=50)
+    class Meta:
+        db_table = 'domain'
+    
+class Release(models.Model):
+    name = models.CharField(max_length=50)
+    stringfrozen = models.BooleanField()
+    status = models.CharField(max_length=12, choices=(('official', 'Official'), ('unofficial', 'Unofficial'), ('external', 'External')))
+    class Meta:
+        db_table = 'release'
+    
+    def total_strings(self):
+        """ Returns the total number of strings in the release as a tuple (doc_total, ui_total) """
+        # TODO: transform this SQL query in a Django model query, if possible...
+        query = """ SELECT domain.dtype, SUM(stat.untranslated) FROM statistics AS stat
+                      LEFT JOIN domain ON domain.id=stat.domain_id
+                      LEFT JOIN branch AS br ON br.id=stat.branch_id
+                      LEFT JOIN category AS cat ON br.category_id=cat.id
+                      LEFT JOIN release AS rel ON rel.id = cat.release_id 
+                      WHERE rel.id=%s AND stat.language_id IS NULL
+                      GROUP BY domain.dtype """
+        cursor = connection.cursor()
+        cursor.execute(query, (self.id,))
+        totaldoc = 0; totalui = 0
+        for row in cursor.fetchall():
+            if row[0] == 'ui':
+                totalui = row[1]
+            elif row[0] == 'doc':
+                totaldoc = row[1]
+        return (totaldoc, totalui)
+    
+    def total_for_lang(self, lang):
+        """ Returns total translated/fuzzy/untranslated strings for a specific language """
+        
+        total_doc, total_ui = self.total_strings()
+        query = """SELECT domain.dtype, SUM(stat.translated), SUM(stat.fuzzy) FROM statistics AS stat
+            LEFT JOIN domain ON stat.domain_id=domain.id
+            LEFT JOIN branch ON stat.branch_id=branch.id
+            LEFT JOIN category ON branch.category_id=category.id
+            WHERE language_id = %s AND category.release_id = %s
+            GROUP BY domain.dtype;"""
+        cursor = connection.cursor()
+        cursor.execute(query, (lang.id, self.id))
+        stats = {'id': self.id, 'name': _(self.name),
+                 'uitrans':0, 'uifuzzy':0, 'uitotal':total_ui,
+                 'doctrans':0, 'docfuzzy':0, 'doctotal':total_doc,
+                 'uitransperc':0, 'uifuzzyperc':0, 'uiuntransperc':0,
+                 'doctransperc':0, 'docfuzzyperc':0, 'docuntransperc':0}
+        for res in cursor.fetchall():
+            if res[0] == 'ui':
+                stats['uitrans'] = res[1]
+                stats['uifuzzy'] = res[2]
+            if res[0] == 'doc':
+                stats['doctrans'] = res[1]
+                stats['docfuzzy'] = res[2]
+        stats['uiuntrans'] = total_ui - (stats['uitrans'] + stats['uifuzzy'])
+        if total_ui > 0:
+            stats['uitransperc'] = 100*stats['uitrans']/total_ui
+            stats['uifuzzyperc'] = 100*stats['uifuzzy']/total_ui
+            stats['uiuntransperc'] = 100*stats['uiuntrans']/total_ui
+        stats['docuntrans'] = total_doc - (stats['doctrans'] + stats['docfuzzy'])
+        if total_doc > 0:
+            stats['doctransperc'] = 100*stats['doctrans']/total_doc
+            stats['docfuzzyperc'] = 100*stats['docfuzzy']/total_doc
+            stats['docuntransperc'] = 100*stats['docuntrans']/total_doc
+        return stats
+    
+    def get_global_stats(self):
+        """ Get statistics for all languages in a release, grouped by language
+            Returns a sorted list: (language, doc_trans, doc_fuzzy, doc_untrans, ui_trans, ui_fuzzy, ui_untrans)"""
+        
+        # TODO: transform this SQL query in a Django model query, if possible...
+        query = """SELECT lang.name, lang.locale, domain.dtype, SUM(stat.translated) AS trans, SUM(stat.fuzzy)
+          FROM statistics AS stat
+          LEFT JOIN domain ON domain.id=stat.domain_id
+          LEFT JOIN language AS lang ON stat.language_id=lang.id
+          LEFT JOIN branch AS br ON br.id=stat.branch_id
+          LEFT JOIN category ON br.category_id=category.id
+          WHERE category.release_id = %s
+          GROUP BY domain.dtype, stat.language_id
+          ORDER BY domain.dtype, trans desc;"""
+        cursor = connection.cursor()
+        cursor.execute(query, (self.id,))
+        stats = {}
+        total_docstrings, total_uistrings = self.total_strings()
+        for row in cursor.fetchall():
+            if not stats.has_key(row[1]):
+                # Initialize stats dict
+                stats[row[1]] = {'lang':row[0], 'lang_code':row[1], 'doc_trans':0, 'doc_fuzzy':0, 'doc_untrans': total_docstrings,
+                                  'doc_percent':0, 'doc_percentfuzzy':0, 'doc_percentuntrans':100,
+                                  'ui_trans':0, 'ui_fuzzy':0, 'ui_untrans': total_uistrings,
+                                  'ui_percent':0, 'ui_percentfuzzy':0, 'ui_percentuntrans':100}
+            if row[2] == 'doc':
+                stats[row[1]]['doc_trans'] = row[3]
+                stats[row[1]]['doc_fuzzy'] = row[4]
+                stats[row[1]]['doc_untrans'] = total_docstrings - (row[3] + row[4])
+                stats[row[1]]['doc_percent'] = 100*row[3]/total_docstrings
+                stats[row[1]]['doc_percentfuzzy'] = 100*row[4]/total_docstrings
+                stats[row[1]]['doc_percentuntrans'] = 100*stats[row[1]]['doc_untrans']/total_docstrings
+            if row[2] == 'ui':
+                stats[row[1]]['ui_trans'] = row[3]
+                stats[row[1]]['ui_fuzzy'] = row[4]
+                stats[row[1]]['ui_untrans'] = total_uistrings - (row[3] + row[4])
+                stats[row[1]]['ui_percent'] = 100*row[3]/total_uistrings
+                stats[row[1]]['ui_percentfuzzy'] = 100*row[4]/total_uistrings
+                stats[row[1]]['ui_percentuntrans'] = 100*stats[row[1]]['ui_untrans']/total_uistrings
+        cursor.close()
+        results = [stat for key, stat in stats.items()]
+        results.sort(self.compare_stats)
+        return results 
+    
+    def compare_stats(self, a, b):
+        res = cmp(b['ui_trans'], a['ui_trans'])
+        if not res:
+            res = cmp(b['doc_trans'], a['doc_trans'])
+            if not res:
+                res = cmp(b['lang'], a['lang'])
+        return res  
+    
+    def get_lang_stats(self, lang):
+        """ Get statistics for a specific language, producing the stats data structure
+            Used for displaying the language-release template """
+        
+        # Sorted by module to allow grouping ('fake' stats)
+        pot_stats = Statistics.objects.filter(language=None, branch__category__release=self).order_by('domain__module__id')
+        stats = {'doc':{'totaltrans':0, 'totalfuzzy':0, 'totaluntrans':0, 'categs':{}}, 
+                 'ui':{'totaltrans':0, 'totalfuzzy':0, 'totaluntrans':0, 'categs':{}} 
+                }
+        for stat in pot_stats:
+            dtype = stat.domain.dtype
+            categdescr = stat.branch.category.description
+            domname = _(stat.domain.description)
+            modname = stat.domain.module.name
+            if not stats[dtype]['categs'].has_key(categdescr):
+                stats[dtype]['categs'][categdescr] = {'cattrans':0, 'catfuzzy':0, 
+                                                      'catuntrans':0, 'modules':{}}
+            stats[dtype]['totaluntrans'] += stat.Untranslated
+            stats[dtype]['categs'][categdescr]['catuntrans'] += stat.Untranslated
+            if not stats[dtype]['categs'][categdescr]['modules'].has_key(modname):
+                # first element is a placeholder for a fake stat
+                stats[dtype]['categs'][categdescr]['modules'][modname] = {' fake':None, domname:stat}
+                previous_domname = domname
+            else:
+                if len(stats[dtype]['categs'][categdescr]['modules'][modname]) < 3:
+                    # Create a fake statistics object for module summary
+                    stats[dtype]['categs'][categdescr]['modules'][modname][' fake'] = FakeStatistics(stat.domain.module, dtype)
+                    stats[dtype]['categs'][categdescr]['modules'][modname][' fake'].untrans(stats[dtype]['categs'][categdescr]['modules'][modname][previous_domname])
+                stats[dtype]['categs'][categdescr]['modules'][modname][domname] = stat
+                stats[dtype]['categs'][categdescr]['modules'][modname][' fake'].untrans(stat)
+            #stats[dtype]['categs'][categdescr]['modules']["%s-%s" % (stat.branch.id, stat.domain.id)] = stat
+        
+        # Second pass for translated stats
+        tr_stats = Statistics.objects.filter(language=lang, branch__category__release=self).order_by('domain__module__id')
+        for stat in tr_stats:
+            dtype = stat.domain.dtype
+            categdescr = stat.branch.category.description
+            domname = _(stat.domain.description)
+            modname = stat.domain.module.name
+            stats[dtype]['totaltrans'] += stat.Translated
+            stats[dtype]['totalfuzzy'] += stat.Fuzzy
+            stats[dtype]['totaluntrans'] -= (stat.Translated + stat.Fuzzy)
+            stats[dtype]['categs'][categdescr]['cattrans'] += stat.Translated
+            stats[dtype]['categs'][categdescr]['catfuzzy'] += stat.Fuzzy
+            stats[dtype]['categs'][categdescr]['catuntrans'] -= (stat.Translated + stat.Fuzzy)
+            if stats[dtype]['categs'][categdescr]['modules'][modname][' fake']:
+                stats[dtype]['categs'][categdescr]['modules'][modname][' fake'].trans(stat)
+            # Replace POT stat by translated stat
+            stats[dtype]['categs'][categdescr]['modules'][modname][domname] = stat
+        
+        # Compute percentages and sorting
+        for dtype in ['ui', 'doc']:
+            stats[dtype]['total'] = stats[dtype]['totaltrans'] + stats[dtype]['totalfuzzy'] + stats[dtype]['totaluntrans']
+            if stats[dtype]['total'] > 0:
+                stats[dtype]['totaltransperc'] = 100*stats[dtype]['totaltrans']/stats[dtype]['total']
+                stats[dtype]['totalfuzzyperc'] = 100*stats[dtype]['totalfuzzy']/stats[dtype]['total']
+                stats[dtype]['totaluntransperc'] = 100*stats[dtype]['totaluntrans']/stats[dtype]['total']
+            else:
+                stats[dtype]['totaltransperc'] = 0
+                stats[dtype]['totalfuzzyperc'] = 0
+                stats[dtype]['totaluntransperc'] = 0
+            for key, categ in stats[dtype]['categs'].items():
+                categ['cattotal'] = categ['cattrans'] + categ['catfuzzy'] + categ['catuntrans']
+                categ['cattransperc'] = 100*categ['cattrans']/categ['cattotal']
+                # Sort modules
+                mods = [[name,mod] for name, mod in categ['modules'].items()]
+                mods.sort()
+                categ['modules'] = mods
+                # Sort domains
+                for mod in categ['modules']:
+                    doms = [(name,dom) for name, dom in mod[1].items()]
+                    doms.sort()
+                    mod[1] = doms
+        return stats
+
+        
+class Category(models.Model):
+    release = models.ForeignKey(Release)
+    description = models.TextField()
+    class Meta:
+        db_table = 'category'
+        
+
+class Statistics(models.Model):
+    # After migration, module, domain, type, branch, language can be deleted
+    branch = models.ForeignKey(Branch) #alter table statistics add branch_id integer REFERENCES "branch" ("id");
+    domain = models.ForeignKey(Domain) #alter table statistics add domain_id integer REFERENCES "domain" ("id");
+    language = models.ForeignKey(Language, null=True) #alter table statistics add language_id integer REFERENCES "language" ("id");
+    
+    Module = models.TextField(db_column='module')
+    # whether this is about a document or UI translation
+    Type = models.CharField(db_column='type', max_length=3, choices=(('doc', 'Documentation'), ('ui', 'User Interface')))
+    Domain = models.TextField(db_column='domain')
+    Branch = models.TextField(db_column='branch')
+    Language = models.CharField(db_column='language', max_length=15)
+    Date = models.DateTimeField(db_column='date', auto_now_add=True)
+    Translated = models.IntegerField(db_column='translated', default=0)
+    Fuzzy = models.IntegerField(db_column='fuzzy', default=0)
+    Untranslated = models.IntegerField(db_column='untranslated', default=0)
+    class Meta:
+        db_table = 'statistics'
+
+    def __init__(self, *args, **kwargs):
+        models.Model.__init__(self, *args, **kwargs)
+        self.figures = None
+        self.modname = None
+        self.partial_po = False # True if part of a multiple po module
+    
+    def __unicode__(self):
+        """ String representation of the object """
+        return "%s (%s-%s) %s (%s)" % (self.Module, self.Type, self.Domain,
+                                       self.Branch, self.Language)
+    
+    def is_fake(self):
+        return False
+        
+    def tr_percentage(self):
+        if self.pot_size() == 0:
+            return 0
+        else:
+            return 100*self.Translated/self.pot_size()
+    
+    def fu_percentage(self):
+        if self.pot_size() == 0:
+            return 0
+        else:
+            return 100*self.Fuzzy/self.pot_size()
+    
+    def un_percentage(self):
+        if self.pot_size() == 0:
+            return 0
+        else:
+            return 100*self.Untranslated/self.pot_size()
+
+    def get_lang(self):
+        if self.language:
+            return _("%(langname)s (%(langcode)s)") % {'langname':self.language.name, 'langcode':self.language.locale}
+        else:
+            return "pot file"
+    
+    def module_name(self):
+        if not self.modname:
+            self.modname = self.branch.module.name
+        return self.modname
+    
+    def module_description(self):
+        return self.branch.module.description
+        
+    def get_translationstat(self):
+        return "%d%%&nbsp;(%d/%d/%d)" % (self.tr_percentage(), self.Translated, self.Fuzzy, self.Untranslated)
+    
+    def filename(self):
+        if self.domain.name == 'po':
+            potbase = self.module_name()
+        else:
+            potbase = self.domain.name
+        if self.Language:
+            return "%s.%s.%s.po" % (potbase, self.branch.name, self.Language)
+        else:
+            return "%s.%s.pot" % (potbase, self.branch.name)
+            
+    def pot_size(self):
+        return int(self.Translated) + int(self.Fuzzy) + int(self.Untranslated)
+    
+    def pot_text(self):
+        msg_text = ungettext(u"%(count)s message", "%(count)s messages", self.pot_size()) % {'count': self.pot_size()}
+        upd_text = _(u"updated on %(date)s") % {'date': self.Date.strftime("%Y-%m-%d %H:%M:%S ")+tzname[0]}
+        if self.fig_count():
+            fig_text = ungettext(u"%(count)s figure", "%(count)s figures", self.fig_count()) % {'count': self.fig_count()}
+            text = _(u"POT file (%(messages)s, %(figures)s) â %(updated)s") % \
+                              {'messages': msg_text, 'figures': fig_text, 'updated': upd_text}
+        else:
+            text = _(u"POT file (%(messages)s) â %(updated)s") % \
+                              {'messages': msg_text, 'updated': upd_text}
+        return text
+    
+    def get_figures(self):
+        if self.figures is None and self.domain.dtype == 'doc':
+            # Extract image strings: beforeline/msgid/msgstr/grep auto output a fourth line 
+            command = "msgcat --no-wrap %(pofile)s| grep -A 1 -B 1 '^msgid \"@@image:'" % { 'pofile': self.po_path() }
+            (error, output) = commands.getstatusoutput(command)
+            lines = output.split('\n')
+            re_path = re.compile('^msgid \"@@image: \'([^\']*)\'')
+            self.figures = []
+            
+            i = 0
+            while i < len(lines):
+                fig = {}
+                fig['fuzzy'] = lines[i]=='#, fuzzy'
+                path_match = re_path.match(lines[i+1])
+                if path_match and len(path_match.groups()):
+                    fig['path'] = path_match.group(1)
+                else:
+                    fig['path'] = '' # This should not happen
+                fig['translated'] = len(lines[i+2])>9 and not fig['fuzzy']
+                # Check if a translated figure really exists or if the English one is used
+                if os.path.exists(os.path.join(self.branch.co_path(), self.domain.directory, self.language.locale, fig['path'])):
+                    fig['translated_file'] = True
+                else: fig['translated_file'] = False
+                self.figures.append(fig)
+                i += 4
+        return self.figures
+    
+    def fig_count(self):
+        """ If stat of a document type, get the number of figures in the document """
+        return len(self.figures())
+    
+    def fig_stats(self):
+        stats = {'fuzzy':0, 'translated':0, 'total':0, 'prc':0}
+        for fig in self.get_figures():
+            stats['total'] += 1
+            if fig['fuzzy']: stats['fuzzy'] += 1
+            else:
+                if fig['translated']: stats['translated'] += 1
+        stats['untranslated'] = stats['total'] - (stats['translated'] + stats['fuzzy'])
+        if stats['total'] > 0:
+            stats['prc'] = stats['translated']/stats['total']
+        return stats
+            
+    def vcs_path(self):
+        """ Return the VCS path of file on remote vcs """
+        return os.path.join(self.branch.get_vcsurl(), self.domain.directory)
+        
+    def vcs_webpath(self):
+        """ Return the Web interface path of file on remote vcs """
+        return os.path.join(self.branch.get_vcsweburl(), self.domain.directory)
+        
+    def po_path(self):
+        """ Return path of po file on local filesystem """
+        subdir = ""
+        if self.domain.dtype == "doc":
+            subdir = "docs"
+        return os.path.join(settings.POTDIR, self.module_name()+'.'+self.branch.name, subdir, self.filename())
+        
+    def po_url(self):
+        """ Return URL of po file, e.g. for downloading the file """
+        if self.domain.dtype == "doc":
+            subdir = "docs/"
+        else:
+            subdir = ""
+        #return self.filename()
+        return "/POT/%s.%s/%s%s" % (self.module_name(), self.branch.name, subdir, self.filename())
+        
+    def most_important_message(self):
+        """ Return a message of type 1.'error', or 2.'warn, or 3.'warn """
+        error = None
+        for e in self.information_set.all():
+            if not error or e.Type == 'error' or (e.Type == 'warn' and error.Type == 'info'):
+                error = e
+        return error
+
+class FakeStatistics(object):
+    """ This is a fake statistics class where a summary value is needed for a multi-domain module
+        This is used in get_lang_stats for the language-release-stats template """
+    def __init__(self, module, dtype):
+        self.module = module
+        self.dtype = dtype
+        self.Translated = 0
+        self.Fuzzy = 0
+        self.Untranslated = 0
+        self.partial_po = False
+    
+    def untrans(self, stat):
+        """ Called for POT file, so only Untranslated is concerned """
+        self.Untranslated += stat.Untranslated
+        stat.partial_po = True
+    
+    def trans(self, stat):
+        self.Translated += stat.Translated
+        self.Fuzzy += stat.Fuzzy
+        self.Untranslated -= (stat.Translated + stat.Fuzzy)
+        stat.partial_po = True
+    
+    def is_fake(self):
+        return True
+        
+    def pot_size(self):
+        return int(self.Translated) + int(self.Fuzzy) + int(self.Untranslated)
+    def tr_percentage(self):
+        if self.pot_size() == 0:
+            return 0
+        else:
+            return 100*self.Translated/self.pot_size()
+    def fu_percentage(self):
+        if self.pot_size() == 0:
+            return 0
+        else:
+            return 100*self.Fuzzy/self.pot_size()    
+    def un_percentage(self):
+        if self.pot_size() == 0:
+            return 0
+        else:
+            return 100*self.Untranslated/self.pot_size()
+    def module_name(self):
+        return self.module.name
+    def module_description(self):
+        return self.module.description
+       
+class ArchivedStatistics(models.Model):
+    Module = models.TextField(db_column='module')
+    Type = models.CharField(db_column='type', max_length=3, choices=(('doc', 'Documentation'), ('ui', 'User Interface')))
+    Domain = models.TextField(db_column='domain')
+    Branch = models.TextField(db_column='branch')
+    Language = models.CharField(db_column='language', max_length=15)
+    Date = models.DateTimeField(db_column='date')
+    Translated = models.IntegerField(db_column='translated', default=0)
+    Fuzzy = models.IntegerField(db_column='fuzzy', default=0)
+    Untranslated = models.IntegerField(db_column='untranslated', default=0)
+    class Meta:
+        db_table = 'archived_statistics'
+
+
+class Information(models.Model):
+    Statistics = models.ForeignKey('Statistics', db_column='statistics_id')
+    Type = models.CharField(db_column='type', max_length=5, 
+                            choices=(('info', 'Information'), ('warn','Warning'), ('error','Error'))) # priority of a stats message
+    Description = models.TextField(db_column='description')
+    class Meta:
+        db_table = 'information'
+
+    def get_icon(self):
+        return "/data/%s.png" % self.Type
+    
+    def get_description(self):
+        text = self.Description
+        matches = re.findall('###([^#]*)###',text) 
+        if matches:
+            text = re.sub('###([^#]*)###', '%s', text)
+
+        text = _(text)
+        
+        #FIXME: if multiple substitutions, works only if order of %s is unchanged in translated string
+        for match in matches:
+        	  text = text.replace('%s',match,1)
+        return text
+
+class ArchivedInformation(models.Model):
+    Statistics = models.ForeignKey('ArchivedStatistics', db_column='statistics_id')
+    Type = models.CharField(db_column='type', max_length=5, 
+                            choices=(('info', 'Information'), ('warn','Warning'), ('error','Error'))) # priority of a stats message
+    Description = models.TextField(db_column='description')
+    class Meta:
+        db_table = 'archived_information'
+

Added: branches/djamnedlies/stats/templates/base.tmpl
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/templates/base.tmpl	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,101 @@
+{% load i18n %}
+<!DOCTYPE html
+     PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd";>
+<html xmlns="http://www.w3.org/1999/xhtml"; xml:lang="{{ defaults.language }}" lang="{{ defaults.language }}">
+<head>
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+  <title>{% block title %}page title{% endblock %}</title>
+  <link rel="icon" type="image/png" href="{{ webroot }}/data/foot-16.png"/>
+  <link rel="stylesheet" href="{{ webroot }}/data/main.css"/>
+  <link rel="stylesheet" href="{{ webroot }}/data/layout.css"/>
+  <link rel="stylesheet" href="{{ webroot }}/data/style.css"/>
+{% if rtl %}
+  <link rel="stylesheet" href="{{ webroot }}/data/rtl.css"/>
+{% endif %}
+  <script src="{{ webroot }}/data/main.js" type="text/javascript"></script>
+</head>
+
+{% if rtl %}
+  <body dir="rtl">
+{% else %}
+  <body>
+{% endif %}
+
+  <div id="page">
+    <ul id="general">
+      <li id="siteaction-gnome_home" class="home">
+        <a href="http://www.gnome.org/";>{% trans "Home" %}</a>
+      </li>
+      <li id="siteaction-gnome_news">
+        <a href="http://news.gnome.org";>{% trans "News" %}</a>
+      </li>
+      <li id="siteaction-gnome_projects">
+        <a href="http://www.gnome.org/projects/";>{% trans "Projects" %}</a>
+      </li>
+      <li id="siteaction-gnome_art">
+        <a href="http://art.gnome.org";>{% trans "Art" %}</a>
+      </li>
+      <li id="siteaction-gnome_support">
+        <a href="http://www.gnome.org/support/";>{% trans "Support" %}</a>
+      </li>
+      <li id="siteaction-gnome_development">
+        <a href="http://developer.gnome.org";>{% trans "Development" %}</a>
+      </li>
+      <li id="siteaction-gnome_community">
+        <a href="http://www.gnome.org/community/";>{% trans "Community" %}</a>
+      </li>
+    </ul>
+    <div id="header">
+      <h1>{% trans "Damned Lies" %}</h1>
+      <div id="tabs">
+        <ul id="portal-globalnav">
+          <li
+{% ifequal pageSection "home" %}
+	  class="selected"
+{% endifequal %}
+            ><a href="{{ webroot }}/" title="{% trans "Back to Damned Lies home page" %}"><span>{% trans "Home" %}</span></a>
+          </li>
+	  <li
+{% ifequal pageSection "teams" %}
+	  class="selected"
+{% endifequal %}
+	   ><a href="{{ webroot }}/teams/"><span>{% trans "Teams" %}</span></a></li>
+	  <li
+{% ifequal pageSection "languages" %}
+	  class="selected"
+{% endifequal %}
+	   ><a href="{{ webroot }}/languages/"><span>{% trans "Languages" %}</span></a></li>
+	  <li
+{% ifequal pageSection "releases" %}
+	  class="selected"
+{% endifequal %}
+	   ><a href="{{ webroot }}/releases/"><span>{% trans "Release sets" %}</span></a></li>
+	  <li
+{% ifequal pageSection "module" %}
+	  class="selected"
+{% endifequal %}
+	   ><a href="{{ webroot }}/module/"><span>{% trans "Modules" %}</span></a></li>
+        </ul>
+      </div> <!-- end of #tabs -->
+    </div> <!-- end of #header -->
+  </div>
+<!-- end site header -->
+
+  <div class="body">
+    <div id="content">
+    {% block content %}
+    {% endblock %}
+    </div>
+
+  <div id="footer">
+    Copyright &copy; 2006, 2007 <a href="http://www.gnome.org/";>{% trans "The GNOME Project" %}</a>.
+    <br /> 
+{% trans "Maintained in the <a href='http://svn.gnome.org/viewvc/damned-lies/'>damned-lies</a> module on <a href='http://svn.gnome.org/'>svn.gnome.org</a>" %}
+ <br />
+    {% trans "Hosted by" %} <a href="http://www.canonical.com/";>Canonical</a>.
+  </div>
+
+</div> <!-- end of div.body -->
+</body>
+</html>

Added: branches/djamnedlies/stats/templates/index.tmpl
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/templates/index.tmpl	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,50 @@
+{% extends "base.tmpl" %}
+{% load i18n %}
+
+{% block title %} {% trans "Damned Lies about GNOME" %} {% endblock %}
+
+{% block content %}
+<div class="mainpage">
+
+<h1>{% trans "Damned Lies about GNOME" %}</h1>
+
+<table><tr><td width="50%" valign="top">
+
+<h2><a href="teams/">{% trans "Teams" %}</a></h2>
+<p>{% trans "List of all GNOME Translation Teams, with full information such as primary contact (coordinator), web pages and mailing list information." %}
+  {% trans "If there is no team for your language, you can easily <a href='http://live.gnome.org/TranslationProject/StartingATeam/'>start your own team</a>." %}
+</p>
+
+</td><td valign="top">
+
+<h2><a href="languages/">{% trans "Languages" %}</a></h2>
+<p>{% trans "List of all languages there are <a href='teams/'>Translation Teams</a> for." %}
+  {% trans "Some teams manage more than one language (usually just simple variants), and here you can see all the languages GNOME is being translated to." %}</p>
+
+</td></tr><tr><td valign="top">
+
+<h2><a href="releases/">{% trans "Release Sets" %}</a></h2>
+<p>{% trans "List of all GNOME release sets and releases we gather stats for." %}</p>
+
+<p>{% trans "Examples of release sets are &quot;GNOME Office&quot;, &quot;Fifth Toe&quot; or &quot;GNOME 2.14&quot;." %}
+  {% trans "Official GNOME release sets are further divided into categories such as &quot;Desktop Applications&quot; and &quot;Developer Platform&quot;." %}</p>
+
+<p>{% trans "Look here if you want to compare language support in any of these release sets." %}</p>
+</td><td valign="top">
+
+<h2><a href="module/">{% trans "Modules" %}</a></h2>
+<p>{% trans "List of all modules with statistics in here." %}</p>
+
+<p>{% trans "Modules are separate libraries or applications, with one or more branches of development included." %}
+  {% trans "They are usually taken from CVS, and we keep all relevant information on them (Bugzilla details, web page, maintainer information,...)." %}</p>
+
+</td></tr></table>
+
+{% if translator_credits %}
+<p class="footnote">
+  {% trans "Translated by:" %} {{ translator_credits }}
+</p>
+{% endif %}
+
+</div>
+{% endblock %}

Added: branches/djamnedlies/stats/templates/language-release-stats.tmpl
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/templates/language-release-stats.tmpl	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,70 @@
+{# parent template has to define variable modstats #}
+
+{% load i18n %}
+
+<table name="stats-table">
+<tr style="background: inherit;">
+  <td class="leftcell" style="font-size: 120%; background: inherit;" colspan="2">{{ modstats.totaltransperc }}% ({{ modstats.totaltrans }}/{{ modstats.totalfuzzy }}/{{ modstats.totaluntrans }})</td>
+  <td style="width: 108px; text-align: center; background:inherit;"><div class="graph">
+      <div class="translated" style="width: {{ modstats.totaltransperc }}px;"></div>
+      <div class="fuzzy" style="left:{{ modstats.totaltransperc }}px; width:{{ modstats.totalfuzzyperc }}px;"></div>
+      {% with modstats.totaltransperc|add:modstats.totalfuzzyperc as upos %}
+      <div class="untranslated" style="left:{{ upos }}px; width: {{ modstats.totaluntransperc }}px;"></div>
+      {% endwith %}
+     </div>
+   </td>
+</tr>
+
+{% for catname,categ in modstats.categs.items %}
+  {% ifnotequal catname "default" %}
+    <tr><td colspan="3" class="leftcell" style="background: inherit; font-size: 120%; font-weight: bold; padding-top:1em;">
+     {% with categ.cattransperc as percentage %}
+     {% blocktrans %}{{ catname }} ({{ percentage }}% translated){% endblocktrans %}
+     {% endwith %}
+    </td></tr>
+  {% endifnotequal %}
+  
+  {% for module in categ.modules %}
+    {% with module.0 as modname %}
+    {% for dom in module.1 %}
+      {% with dom.0 as domname %}
+      {% if dom.1 %}
+        {% ifequal dom.1.tr_percentage 100 %}
+          <tr id="{{ modname }}-{{ domname }}-complete">
+        {% else %}
+          <tr>
+        {% endifequal %}
+          {% if dom.1.partial_po %}
+            {# This is a partial po, indented, with the domain description #}
+            <td class="leftcell" style="padding-left:2em; padding-right:2em;">
+            <a href="{{ dom.1.po_url }}"><img src="/data/download.png" alt="{% trans "Download po file" %}"></a>
+            {{ dom.1.domain.description }}
+            </td>
+          {% else %}
+            <td class="leftcell">
+            {% if not dom.1.is_fake %}
+              <a href="{{ dom.1.po_url }}"><img src="/data/download.png" alt="{% trans "Download po file" %}"></a>
+              <a href="{% url stats.views.module modname %}">
+            {% else %}
+              <a href="{% url stats.views.module modname %}" style="font-style: italic;">
+            {% endif %}
+            {{ dom.1.module_description }}</a></td>
+          {% endif %}
+          <td>{{ dom.1.tr_percentage }}%&nbsp;({{ dom.1.Translated }}/{{ dom.1.Fuzzy }}/{{ dom.1.Untranslated }})</td>
+          <td style="width: 108px; text-align: center; background:inherit;"><div class="graph">
+              <div class="translated" style="width: {{ dom.1.tr_percentage }}px;"></div>
+              <div class="fuzzy" style="left:{{ dom.1.tr_percentage }}px; width:{{ dom.1.fu_percentage }}px;"></div>
+              {% with dom.1.tr_percentage|add:dom.1.fu_percentage as upos %}
+              <div class="untranslated" style="left:{{ upos }}px; width:{{ dom.1.un_percentage }}px;"></div>
+              {% endwith %}
+             </div>
+           </td>
+        </tr>
+      {% endif %}
+      {% endwith %}
+    {% endfor %}
+    {% endwith %}
+  {% endfor %}
+{% endfor %}
+</table>
+

Added: branches/djamnedlies/stats/templates/language-release.tmpl
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/templates/language-release.tmpl	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,85 @@
+{% extends "base.tmpl" %}
+{% load i18n %}
+
+{% block title %} {% trans release.name %} - {% trans language.name %}{% endblock %}
+
+{% block content %}
+<div class="mainpage">
+
+{% with language.locale as lcode %}
+{% if language.team %}
+  <h1>{% blocktrans with language.team.description as lang %}{{ lang }} Translation Team â {{ lcode }}{% endblocktrans %}</h1>
+
+<table><tr><td valign="top" width="50%">
+  <h2>{% trans "Details" %}</h2>
+
+  {% if language.team.webpage_url %}
+  <strong>{% blocktrans with language.team.description as lang %}{{ lang }} Translation Team Page:{% endblocktrans %}</strong> 
+  <a href="{{ language.team.webpage_url }}">{{ language.team.webpage_url }}</a><br />
+  {% endif %}
+
+  <br/><strong>{% trans "Bugzilla:" %}</strong>
+  <ul>
+    <li><a href="{{ language.bugs_url_enter|safe }}">{% trans "Report Bug in Translation" %}</a></li>
+    <li><a href="{{ language.bugs_url_show|safe }}">{% trans "Show Existing Bugs" %}</a></li>
+  </ul>
+
+  {% if language.team.mailing_list %}
+  <br/><strong>{% trans "Mailing List:" %}</strong>
+  <ul>
+    <li><a href="mailto:{{ language.team.mailing_list }}">{% trans "Send e-mail to the list" %}</a></li>
+    {% if language.team.mailing_list_subscribe %}
+    <li><a href="{{ language.team.mailing_list_subscribe }}">{% trans "Subscribe" %}</a></li>
+    {% endif %}
+  </ul>
+  {% endif %}
+
+  </td><td valign="top">
+  {% if language.team.coordinator %}
+    <h2>{% trans "Coordinator" %}</h2>
+    {% with language.team.coordinator as person %}
+    {% with 0 as printroles %}
+    {% include "person-base.tmpl" %}
+    {% endwith %}
+    {% endwith %}
+  {% endif %}
+</td></tr></table>
+{% else %}
+  <h1>{% blocktrans with language.locale as code %}Language code: {{ code }}{% endblocktrans %}</h1>
+  <p>{% blocktrans %}There is no translation team in charge of '{{ lcode }}' translation.{% endblocktrans %}</p>
+{% endif %}
+{% endwith %}
+
+<h2>{% trans release.name %}</h2>
+
+<p id="hide">
+   <a href="#" onclick="return showHideCompleted();">{% trans "Hide completed modules" %}</a>
+</p>
+
+<p id="show" style="display:none">
+   <a href="#" onclick="return showHideCompleted();">{% trans "Show completed modules" %}</a>
+</p>
+
+{% ifequal release.status "external" %}
+<p><i>{% trans "The modules of this release are not part of the GNOME SVN repository. Please check each module's web page to see where to send translations." %}</i></p>
+{% endifequal %}
+
+<table><tr>
+{% if stats.doc.total %}
+  <td valign="top" width="50%"><!-- two columns set-up -->
+  <h3>{% trans "Documentation" %}</h3>
+  {% with stats.doc as modstats %}
+    {% include "language-release-stats.tmpl" %}
+  {% endwith %}
+  </td>
+{% endif %}  
+  <td valign="top"><!-- second column -->
+
+  <h3>{% trans "UI translations" %}</h3>
+  {% with stats.ui as modstats %}
+    {% include "language-release-stats.tmpl" %}
+  {% endwith %}
+</td></tr></table><!-- end two column layout -->
+
+</div>
+{% endblock %}

Added: branches/djamnedlies/stats/templates/list-languages.tmpl
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/templates/list-languages.tmpl	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,32 @@
+{% extends "base.tmpl" %}
+{% load i18n %}
+
+{% block title %} {% trans "GNOME Languages" %} {% endblock %}
+
+{% block content %}
+<div class="mainpage">
+
+<h1>{% trans "GNOME Languages" %}</h1>
+
+{% blocktrans count languages|length as numb %}
+GNOME is being translated to following {{ numb }} language.
+{% plural %}
+GNOME is being translated to following {{ numb }} languages.
+{% endblocktrans %}
+
+<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3">
+<ul class="foot">
+{% for lang in languages %}
+<li style="font-size: 120%;">
+  {% if lang.name %}
+  <a href="{{ webroot }}/languages/{{ lang.locale }}">{{ lang.translated_name }}</a>
+  {% else %}
+  <a href="{{ webroot }}/languages/{{ lang.locale }}">{{ lang.locale }}</a>
+  {% endif %}
+</li>
+{% endfor %}
+</ul>
+</div>
+
+</div>
+{% endblock %}

Added: branches/djamnedlies/stats/templates/list-modules.tmpl
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/templates/list-modules.tmpl	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,22 @@
+{% extends "base.tmpl" %}
+{% load i18n %}
+
+{% block title %} {% trans "GNOME Modules" %} {% endblock %}
+
+{% block content %}
+<div class="mainpage">
+
+<h1>{% trans "GNOME modules" %}</h1>
+
+<p>{% trans "Select a module below to see some of the damned lies about it:" %}</p>
+
+<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3">
+<ul class="foot">
+{% for mod in modules %}
+  <li><a href="{% url djamnedlies.stats.views.module mod.name %}">{{ mod.translated_name }} </a></li>
+{% endfor %}
+</ul>
+</div>
+
+</div>
+{% endblock %}

Added: branches/djamnedlies/stats/templates/list-releases.tmpl
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/templates/list-releases.tmpl	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,25 @@
+{% extends "base.tmpl" %}
+{% load i18n %}
+
+{% block title %} {% trans "GNOME Releases" %} {% endblock %}
+
+{% block content %}
+<div class="mainpage">
+
+<h1>{% trans "GNOME Releases" %}</h1>
+
+<p>{% trans "Select a release or a release set below to see more details about it:" %}</p>
+
+<ul class="foot">
+{% for rel in releases %}
+  {% ifchanged rel.status %}
+    </ul>
+    <ul class="foot">
+  {% endifchanged %}
+  <li style="font-size: 120%;">
+    <a href="{% url stats.views.release rel.id %}">{% trans rel.name %}</a>
+  </li>
+{% endfor %}
+
+</div>
+{% endblock %}

Added: branches/djamnedlies/stats/templates/list-teams.tmpl
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/templates/list-teams.tmpl	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,33 @@
+{% extends "base.tmpl" %}
+{% load i18n %}
+
+{% block title %} {% trans "GNOME Translation Teams" %} {% endblock %}
+
+{% block content %}
+<div class="mainpage">
+
+<h1>{% trans "GNOME Translation Teams" %}</h1>
+
+{% if teams %}
+<p>{% trans "Select a team below to see more information about it:" %}</p>
+
+    <ul class="foot">
+    {% for team in teams %}
+    <li style="font-size: 120%;">
+      <a href="{% url stats.views.team team.lang_code %}">{{ team.translated_name }}</a>
+      {% if team.webpage_url %}
+        &mdash; <a href="{{ team.webpage_url }}">{{ team.webpage_url }}</a>
+      {% endif %}
+      {% if team.coordinator_url %}
+        <br /><span style="font-size: 80%;">{% blocktrans with team.coordinator_url|safe as url %}Coordinated by {{ url }}{% endblocktrans %}</span>
+      {% endif %}
+    </li>
+    {% endfor %}
+    </ul>
+
+{% else %}
+<p>{% trans "There are currently no translation teams in GNOME. :(" %}</p>
+{% endif %}
+
+</div>
+{% endblock %}

Added: branches/djamnedlies/stats/templates/module-images.tmpl
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/templates/module-images.tmpl	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,58 @@
+{% extends "base.tmpl" %}
+{% load i18n %}
+
+{% block title %} {% blocktrans with module.description as name %}Module Doc Figure Status: {{ name }}{% endblocktrans %} {% endblock %}
+
+{% block content %}
+<div class="mainpage">
+
+<h1><a href="{% url stats.views.module module.name %}">{{ module.description }}</a> {{ stat.domain.description }} ({% trans stat.language.name %})</h1>
+
+{% with stat.fig_stats as figstat %}
+<h2>{% trans "Figures translation status" %} &mdash; 
+    <small>{{ figstat.prc }}% ({{ figstat.translated }}/{{ figstat.fuzzy}}/{{ figstat.untranslated }})</small></h2>
+{% endwith %}
+
+<table class="stats">
+<thead><tr><th width="50%">{% trans "Original" %}</th><th width="50%">{% trans stat.language.name %}</th></tr></thead>
+{% for fig in stat.get_figures %}
+  {% with stat.language.locale as locale %}
+  {% with stat.vcs_webpath as vcswebpath %}
+  {% with stat.vcs_path as vcspath %}
+  <tr><td valign="top">
+    <a href="{{ vcswebpath }}/C/{{ fig.path }}"><span class="path">C/{{ fig.path }}</span><br/></a>
+    <a href="{{ vcspath }}/C/{{ fig.path }}"><img class="screenshot" src="{{ vcspath }}/C/{{ fig.path }}"/></a>
+    </td>
+    <td valign="top" class="$tdclass">
+    {% if fig.translated %}
+      {% if fig.translated_file %}
+      <a href="{{ vcswebpath }}/{{ locale }}/{{ fig.path }}"><span class="path">{{ locale }}/{{ fig.path }}</span></a><br/>
+      <a href="{{ vcspath }}/{{ locale }}/{{ fig.path }}"><img class="screenshot" src="{{ vcspath }}/{{ locale }}/{{ fig.path }}"/></a>
+      {% else %}
+      <p><em>{% trans "Translated, but uses original one (maybe the figure doesn't contain any string to translate)" %}</em></p>
+      {% endif %}
+    {% else %}
+      {% if fig.fuzzy %}
+        <em>{% trans "Fuzzy" %}</em><br/>
+        {% if fig.translated_file %}
+      <a href="{{ vcswebpath }}/{{ locale }}/{{ fig.path }}"><span class="path">{{ locale }}/{{ fig.path }}</span></a><br/>
+      <a href="{{ vcspath }}/{{ locale }}/{{ fig.path }}"><img class="screenshot" src="{{ vcspath }}/{{ locale }}/{{ fig.path }}"/></a>
+        {% else %}
+      <p><em><small>{% trans "No existing file (&quot;Technical&quot; fuzzy)" %}</small></em></p>
+        {% endif %}
+      {% else %}
+        <em>{% trans "Not translated" %}</em><br/>
+        <img src="/data/emptyimg.png">
+      {% endif %}
+    {% endif %}
+    </td>
+  </tr>
+  {% endwith %}
+  {% endwith %}
+  {% endwith %}
+{% endfor %}
+</table>
+
+</div>
+
+{% endblock %}

Added: branches/djamnedlies/stats/templates/module.tmpl
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/templates/module.tmpl	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,100 @@
+{% extends "base.tmpl" %}
+{% load i18n %}
+
+{% block title %} {% blocktrans with module.description as name %}Module Statistics: {{ name }}{% endblocktrans %} {% endblock %}
+
+{% block content %}
+<div class="mainpage">
+
+<h1>{{ module.translated_name }}</h1>
+
+{% if module.comment %}
+  <p>{{ module.comment|safe }}</p>
+{% else %}
+  {% ifnotequal module.vcs_root "http://svn.gnome.org/svn"; %}
+  <p><i><img src="{{ webroot }}/data/warn.png" alt="Warning logo" />{% trans "This module is not part of the GNOME SVN repository. Please check the module's web page to see where to send translations." %}</i></p>
+  {% endifnotequal %}
+{% endif %}
+
+{% if module.homepage %}
+  <p><a href="{{ module.homepage }}">{{ module.homepage }}</a></p>
+{% endif %}
+
+<table><tr><td width="50%" valign="top">
+
+{% if module.maintainers.all %}
+  <h2>{% trans "Maintainers" %}</h2>
+  {% for maintainer in module.maintainers.all %}
+    <div class="maintainer">
+    {% if maintainer.image %}
+      <img class="people" src="{{ maintainer.image }}" alt="" />
+    {% else %}
+      <img class="people" src="{{ webroot }}/data/nobody.png" alt="" />
+    {% endif %}
+    <a style="font-size: 140%;" href="{% url stats.views.person maintainer.id %}">{{ maintainer.name }}</a>
+    </div>
+  {% endfor %}
+{% endif %}
+
+</td><td width="50%" valign="top">
+
+{% if module.bugs_base %}
+  <h2>{% trans "Bug reporting" %}</h2>
+  <ul>
+  {% if module.get_bugs_i18nurl %}
+    <li><a href="{{ module.get_bugs_i18nurl }}">{% trans "Show existing i18n and l10n bugs" %}</li>
+  {% endif %}
+  <li><a href="{{ module.get_bugs_enterurl }}">{% trans "Report bugs to Bugzilla" %}</a></li>
+  </ul>
+{% endif %}
+
+</td></tr></table>
+
+{% if module.branch_set.all %}
+  <!-- Links to branches of module -->
+  <p><b>{% trans "Branches:" %}</b>
+  {% for branch in module.get_branches %}
+    {% ifnotequal forloop.counter 1 %} 
+    - 
+    {% endifnotequal %}
+    <a href="#{{ branch.name }}">{{ branch.name }}</a>
+  {% endfor %}
+  </p>
+
+  <!-- Main loop through branches -->
+  {% for branch in module.get_branches %}
+    <h2><a name="{{ branch.name }}"></a>{{ branch.name }}
+    {% ifequal module.vcs_type "cvs" %}
+      (<a href="{{ branch.get_vcsweburl }}">{% trans "Browse CVS" %}</a>)
+    {% endifequal %}
+    {% ifequal module.vcs_type "svn" %}
+      (<a href="{{ branch.get_vcsweburl }}">{% trans "Browse SVN" %}</a>)
+    {% endifequal %}
+    </h2>
+    {% if branch.release_cat.release.stringfrozen %}
+      <p>{% trans "This branch is currently string-frozen." %}</p>
+    {% endif %}
+
+    <table><tr><td valign="top"><!-- split to two columns -->
+    {% with branch.get_docstats as stats %}
+    {% if stats|length %}
+      <h3>{% trans "Documentation" %}</h3>
+        {% include "show-stats.tmpl" %}
+    {% endif %}
+    {% endwith %}
+
+    </td><td valign="top"><!-- split to two columns -->
+    {% with branch.get_uistats as stats %}
+    {% if stats|length %}
+      <h3>{% trans "Translation" %}</h3>
+        {% include "show-stats.tmpl" %}
+    {% endif %}
+    {% endwith %}
+
+    </td></tr></table>
+
+  {% endfor %}
+{% endif %}
+
+</div>
+{% endblock %}

Added: branches/djamnedlies/stats/templates/people.tmpl
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/templates/people.tmpl	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,30 @@
+{% extends "base.tmpl" %}
+{% load i18n %}
+
+{% block title %} {% trans "GNOME Contributors" %} {% endblock %}
+
+{% block content %}
+<div class="mainpage">
+
+<h2>{% trans "GNOME Contributors" %}</h2>
+
+<p>{% trans "GNOME is being developed by following people:" %}</p>
+
+<div style="column-count:3;-moz-column-count:3;-webkit-column-count:3">
+{% for person in people %}
+  <div class="maintainer">
+  {% ifnotequal person.image '/data/nobody.png' %}
+    <img class="people" src="{{ person.image }}" alt="{{ person.name }}" />
+  {% else %}
+    <img class="people" src="{{ webroot }}/data/nobody.png" alt="" />
+  {% endifnotequal %}
+  <a style="font-size: 120%;" href="{{ webroot }}/people/{{ person.id }}">{{ person.name }}</a><br />
+  {% if person.webpage_url %}
+  <a href="{{ person.webpage_url }}">{{ person.webpage_url }}</a><br />
+  {% endif %}
+  </div>
+
+{% endfor %}
+</div>
+</div>
+{% endblock %}

Added: branches/djamnedlies/stats/templates/person-base.tmpl
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/templates/person-base.tmpl	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,60 @@
+{# Variables $person and $printroles must be defined prior to calling this template #}
+{# If $printroles !=0 $roles must also be defined #}
+{# This template is included in person.tmpl, team.tmpl and language-release.tmpl #}
+
+{% load i18n %}
+
+  <div class="maintainer">
+  {% ifnotequal person.image '/data/nobody.png' %}
+    <img class="people" src="{{ person.image }}" alt="{{ person.name }}" />
+  {% else %}
+    <img class="people" src="{{ webroot }}/data/nobody.png" alt="" />
+  {% endifnotequal %}
+
+  <a style="font-size: 140%;" href="{{ webroot }}/people/{{ person.id }}">{{ person.name }}</a><br />
+
+  {% if person.nospamemail %}
+    <a href="mailto:{{ person.nospamemail }}">{{ person.nospamemail }}</a><br />
+  {% endif %}
+  {% if person.webpage_url %}
+    <a href="{{ person.webpage_url }}">{{ person.webpage_url }}</a><br />
+  {% endif %}
+
+  <br />
+  {% if person.irc_nick %}
+    <strong>{% trans "Instant messaging:" %}</strong>
+    <ul>
+    <li><em>{{ person.irc_nick }}</em> (IRC)</li>
+    </ul>
+  {% endif %}
+
+  {% if person.nospambugzillaaccount %}
+    <strong>{% trans "Bugzilla account:" %}</strong> {{ person.nospambugzillaaccount }}<br />
+  {% endif %}
+
+  {% if person.svn_account %}
+  <strong>{% trans "SVN account:" %}</strong> <a href="http://cia.vc/stats/author/{{ person.svn_account }}">{{ person.svn_account}}</a><br />
+  {% endif %}
+
+  {% if printroles %}
+      {% if roles.maintains %}
+      <h2>{% trans "Maintains:" %}</h2>
+      <ul>
+        {% for module in roles.maintains %}
+        <li><a href="{% url stats.views.module module.name%} ">{{ module.description }}</a></li>
+        {% endfor %}
+      </ul>
+      {% endif %}
+
+      {% if roles.translates %}
+      <h2>{% trans "Translates:" %}</h2>
+      <ul>
+        {% for team in roles.translates %}
+          {% url djamnedlies.stats.views.team team.lang_code as team_url %}
+          <li>{% blocktrans with team.description as teamdesc %}Coordinates <a href="{{ team_url }}">{{ teamdesc }}</a>{% endblocktrans %}
+        {% endfor %}
+      </ul>
+      {% endif %}
+  {% endif %}
+
+</div>

Added: branches/djamnedlies/stats/templates/person.tmpl
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/templates/person.tmpl	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,16 @@
+{% extends "base.tmpl" %}
+{% load i18n %}
+
+{% block title %} {% trans "GNOME Contributor" %} {% endblock %}
+
+{% block content %}
+<div class="mainpage">
+
+  <h2>{{ person.name}}</h2>
+  {% with 1 as printroles %}
+  {% include "person-base.tmpl" %}
+  {% endwith %}
+
+</div>
+
+{% endblock %}

Added: branches/djamnedlies/stats/templates/release.tmpl
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/templates/release.tmpl	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,54 @@
+{% extends "base.tmpl" %}
+{% load i18n %}
+
+{% block title %} {% trans release.name %} {% endblock %}
+
+{% block content %}
+<div class="mainpage">
+
+
+<h1>{% blocktrans with release.name as name %}{{ name }} Release{% endblocktrans %}</h1>
+
+{% ifequal release.status "external" %}
+  <p><i>{% trans "The modules of this release are not part of the GNOME SVN repository. Please check each module's web page to see where to send translations." %}</i></p>
+{% endifequal %}
+
+<table class="stats">
+<thead><tr><th>{% trans "Release" %}</th><th>{% trans "Documentation" %}</th><th>{% trans "Graph" %}</th><th>{% trans "User Interface" %}</th><th>{% trans "Graph" %}</th></tr></thead>
+
+{% for lstats in release.get_global_stats %}
+<tr>
+  <td class="leftcell" style="font-size:120%;">
+    <a href="{{ webroot }}/languages/{{ lstats.lang_code }}/{{ release.id }}">{% trans lstats.lang %}</a>
+  </td>
+  
+  {% ifnotequal lstats.doc_trans|add:lstats.doc_fuzzy "0" %}
+  <td>{{ lstats.doc_percent }}% ({{ lstats.doc_trans }}/{{ lstats.doc_fuzzy }}/{{ lstats.doc_untrans }})</td>
+  {% else %}
+  <td>-</td>
+  {% endifnotequal %}
+  
+  <td style="width: 108px; text-align: center;"><div class="graph">
+      <div class="translated" style="width: {{ lstats.doc_percent }}px;"></div>
+      <div class="fuzzy" style="left:{{ lstats.doc_percent }}px; width:{{ lstats.doc_percentfuzzy }}px;"></div>
+      {% with lstats.doc_percent|add:lstats.doc_percentfuzzy as upos %}
+      <div class="untranslated" style="left:{{ upos }}px; width: {{ lstats.doc_percentuntrans }}px;"></div>
+      {% endwith %}
+     </div>
+   </td>
+  
+  <td>{{ lstats.ui_percent }}% ({{ lstats.ui_trans }}/{{ lstats.ui_fuzzy }}/{{ lstats.ui_untrans }})</td>
+  <td style="width: 108px; text-align: center;"><div class="graph">
+      <div class="translated" style="width: {{ lstats.ui_percent }}px;"></div>
+      <div class="fuzzy" style="left:{{ lstats.ui_percent }}px; width:{{ lstats.ui_percentfuzzy }}px;"></div>
+      {% with lstats.ui_percent|add:lstats.ui_percentfuzzy as upos %}
+      <div class="untranslated" style="left:{{ upos }}px; width: {{ lstats.ui_percentuntrans }}px;"></div>
+      {% endwith %}
+     </div>
+   </td>
+</tr>
+{% endfor %}
+</table>
+
+</div>
+{% endblock %}

Added: branches/djamnedlies/stats/templates/show-stats.tmpl
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/templates/show-stats.tmpl	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,61 @@
+{% load i18n %}
+
+{% for dname, stat in stats.items %}
+  {% with stat|first as stat1 %} 
+  <h3>{% trans stat1.domain.description %}
+  {% ifnotequal stat1.domain.directory 'help' %}
+    {% ifnotequal stat1.domain.directory 'po' %}
+    <br /><span class='path'>{{ stat1.domain.directory }}</span>
+    {% endifnotequal %}
+  {% endifnotequal %}</h3>
+  <a href="{{ stat1.po_url }}"><img src="/data/download.png" alt="Download POT file" /></a>
+  {{ stat1.pot_text }}
+  
+  <!-- /* This is the title of the section that lists notices about a module */ -->
+  {% if stat1.information_set.all %}
+    <h4>{% trans "Notices" %}</h4>
+    <table>
+    {% for msg in stat1.information_set.all %}
+      <tr><td valign="top"><img src="{{ msg.get_icon }}" alt="{{ msg.Type }}" /></td><td>{{ msg.get_description|safe }}</td></tr>
+    {% endfor %}
+    </table>
+  {% endif %}
+
+<table class="stats">
+<thead><tr><th>{% trans "Language" %}</th><th>{% trans "Translated" %}</th>
+ {% if stat1.fig_count %}
+    <th></th>
+ {% endif %}
+<th>{% trans "Graph" %}</th></tr></thead>
+
+    {% for line in stat %}
+      {% if not forloop.first %}
+      <tr>
+      <td class="leftcell"><a href="{{ line.po_url }}">{{ line.get_lang }}</a>
+      {% with line.most_important_message as msg %} 
+      {% if msg %}
+      <img src="{{ msg.get_icon }}" title="{{ msg.get_description }}" alt="{{ msg.Type }}" />
+      {% endif %}
+      {% endwith %}
+      </td>
+      <td>{{ line.get_translationstat|safe }}</td>
+      {% if stat1.fig_count %}
+       <td><a href="/module/{{ module.name }}/{{ stat1.domain.name }}/{{ branch.name }}/{{ line.language.locale}}/images">
+           <img src="/data/figure.png" alt="{% trans "Display document figures" %}"></a></td>
+      {% endif %}
+      <td style="width: 108px; text-align: center;"><div class="graph">
+        <div class="translated" style="width: {{ line.tr_percentage }}px;"></div>
+        <div class="fuzzy" style="left:{{ line.tr_percentage }}px; width:{{ line.fu_percentage }}px;"></div>
+        <div class="untranslated" style="left:{{ line.tr_percentage|add:line.fu_percentage }}px; width: {{ line.un_percentage }}px;"></div>
+        </div>
+      </td>
+      </tr>
+      
+      {% endif %}
+    {% endfor %}
+  {% endwith %}
+
+</table>
+
+{% endfor %}
+

Added: branches/djamnedlies/stats/templates/team.tmpl
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/templates/team.tmpl	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,87 @@
+{% extends "base.tmpl" %}
+{% load i18n %}
+
+{% block title %}{% trans team.description %}{% endblock %}
+
+{% block content %}
+<div class="mainpage">
+
+<h1>{% blocktrans with team.description as lang %}{{ lang }} Translation Team{% endblocktrans %}</h1>
+
+
+<table><tr><td valign="top" width="50%">
+  <h2>{% trans "Details" %}</h2>
+
+  {% if team.webpage_url %}
+  <strong>{% blocktrans with team.description as lang %}{{ lang }} Translation Team Page:{% endblocktrans %}</strong> 
+  <a href="{{ team.webpage_url }}">{{ team.webpage_url }}</a><br />
+  {% endif %}
+
+  <br/><strong>{% trans "Bugzilla:" %}</strong>
+  <ul>
+    <li><a href="{{ language.bugs_url_enter|safe }}">{% trans "Report Bug in Translation" %}</a></li>
+    <li><a href="{{ language.bugs_url_show|safe }}">{% trans "Show Existing Bugs" %}</a></li>
+  </ul>
+
+  {% if team.mailing_list %}
+  <br/><strong>{% trans "Mailing List:" %}</strong>
+  <ul>
+    <li><a href="mailto:{{ team.mailing_list }}">{% trans "Send e-mail to the list" %}</a></li>
+    {% if team.mailing_list_subscribe %}
+    <li><a href="{{ team.mailing_list_subscribe }}">{% trans "Subscribe" %}</a></li>
+    {% endif %}
+  </ul>
+  {% endif %}
+  
+  </td><td valign="top">
+  {% if team.coordinator %}
+    <h2>{% trans "Coordinator" %}</h2>
+    {% with team.coordinator as person %}
+    {% with 0 as printroles %}
+    {% include "person-base.tmpl" %}
+    {% endwith %}
+    {% endwith %}
+  {% endif %}
+</td></tr></table>
+
+{% for lang in team.language_set.all %}
+  <h2>{% trans lang.name %}</h2>
+  <table class="stats">
+  <thead><tr><th>{% trans "Release" %}</th><th>{% trans "Documentation" %}</th><th>{% trans "Graph" %}</th>
+         <th>{% trans "User Interface" %}</th><th>{% trans "Graph" %}</th></tr></thead>
+  {% for stat in lang.get_release_stats %}
+    <tr>
+      <td class="leftcell" style="font-size:120%;">
+        <a href="{{ webroot }}/languages/{{ lang.locale }}/{{ stat.id }}">{{ stat.name }}</a>
+      </td>
+  
+    {% if stat.doctotal %}
+      <td>{{ stat.doctransperc }}% ({{ stat.doctrans }}/{{ stat.docfuzzy }}/{{ stat.docuntrans }})</td>
+      <td style="width: 108px; text-align: center;"><div class="graph">
+        <div class="translated" style="width:{{ stat.doctransperc }}px;"></div>
+        <div class="fuzzy" style="left:{{ stat.doctransperc }}px; width:{{ stat.docfuzzyperc }}px;"></div>
+        {% with stat.doctransperc|add:stat.docfuzzyperc as upos %}
+        <div class="untranslated" style="left:{{ upos }}px; width:{{ stat.docuntransperc }}px;"></div>
+        {% endwith %}
+        </div>
+      </td>
+    {% else %}
+      <td>-</td><td></td>
+    {% endif %}
+
+    <td>{{ stat.uitransperc }}% ({{ stat.uitrans }}/{{ stat.uifuzzy }}/{{ stat.uiuntrans }})</td>
+    <td style="width: 108px; text-align: center;"><div class="graph">
+      <div class="translated" style="width:{{ stat.uitransperc }}px;"></div>
+      <div class="fuzzy" style="left:{{ stat.uitransperc }}px; width:{{ stat.uifuzzyperc }}px;"></div>
+      {% with stat.uitransperc|add:stat.uifuzzyperc as upos %}
+      <div class="untranslated" style="left:{{ upos }}px; width:{{ stat.uiuntransperc }}px;"></div>
+      {% endwith %}
+      </div>
+    </td>
+  </tr>
+  {% endfor %}
+</table>
+{% endfor %}
+
+</div>
+{% endblock %}

Added: branches/djamnedlies/stats/urls.py
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/urls.py	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,29 @@
+from django.conf.urls.defaults import *
+
+# Uncomment the next two lines to enable the admin:
+from django.contrib import admin
+admin.autodiscover()
+
+urlpatterns = patterns('djamnedlies.stats.views',
+    (r'^/*$', 'index'),
+    (r'^teams/*$', 'teams'),
+    (r'^teams/(?P<langcode>.*)$', 'team'),
+    (r'^languages/*$', 'languages'),
+    (r'^languages/(?P<langcode>\w+)/$', 'team'),
+    (r'^languages/(?P<langcode>\w+)/(?P<release_id>\d+)/$', 'languagerelease'),
+    (r'^people/*$', 'people'),
+    (r'^people/(?P<person_id>.*)$', 'person'),
+    (r'^module/*$', 'modules'),
+    (r'^module/(?P<module_name>[^/]+)$', 'module'),
+    (r'^module/(?P<module_name>\w+)/(?P<potbase>\w+)/(?P<branch_name>\w+)/(?P<langcode>\w+)/images/$', 'docimages'),
+    (r'^releases/*$', 'releases'),
+    (r'^releases/(?P<release_id>.*)$', 'release'),
+)
+    # Static files (should be replaced by web server redirection in production
+urlpatterns += patterns('',
+    (r'^data/(?P<path>.*)$', 'django.views.static.serve',
+        {'document_root': '/home/claude/Sauvegarde/Sites/djamnedlies/stats/data'}),
+    (r'^POT/(?P<path>.*)$', 'django.views.static.serve',
+        {'document_root': '/home/claude/www/damned-lies/cvs/POT'}),
+
+)

Added: branches/djamnedlies/stats/utils.py
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/utils.py	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2008 Claude Paroz <claude 2xlibre net>.
+#
+# This file is part of Damned Lies.
+#
+# Damned Lies is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# Damned Lies is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Damned Lies; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from django.utils.translation import ugettext as _
+import re, time
+
+def obfuscateEmail(email):
+    if email:
+        return email.replace('@', ' at ').replace('.', ' dot ')
+    return ""
+
+def sortObjectList(lst, tr_field):
+    """ Sort an object list with translated_name """
+    for l in lst:
+        l.translated_name = _(getattr(l, tr_field))
+    templist = [(obj_.translated_name.lower(), obj_) for obj_ in lst]
+    templist.sort()
+    return [obj_ for (key1, obj_) in templist]
+
+def multiple_replace(dct, text):
+    regex = re.compile("(%s)" % "|".join(map(re.escape, dct.keys())))
+    return regex.sub(lambda mo: dct[mo.string[mo.start():mo.end()]], text)
+
+def stripHTML(string):
+    replacements = {"<ul>": "\n", "</ul>": "\n", "<li>": " * ", "\n</li>": "", "</li>": ""}
+    return multiple_replace(replacements, string)
+
+class Profiler():
+    def __init__(self):
+        self.start = time.clock()
+    
+    def time_spent(self):
+        return time.clock() - self.start 

Added: branches/djamnedlies/stats/views.py
==============================================================================
--- (empty file)
+++ branches/djamnedlies/stats/views.py	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,134 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2008 Claude Paroz <claude 2xlibre net>.
+#
+# This file is part of Damned Lies.
+#
+# Damned Lies is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# Damned Lies is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Damned Lies; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+from django.shortcuts import render_to_response
+from stats.models import Statistics, Team, Language, Person, Module, Release
+from stats.conf import settings
+from djamnedlies.stats import utils, defaults
+from django.http import HttpResponse
+from django.utils.translation import ugettext_lazy as _
+
+def index(request):
+    translator_credit = _("translator-credits")
+    if defaults.language == 'en' or translator_credit == "translator-credits":
+        translator_credit = ""
+    else:
+        translator_credit.replace("\n", ", ").replace("<", "&lt;").replace(">", "&gt;")
+    context = {'pageSection': "home",
+               'webroot': settings.WEBROOT,
+               'rtl': False,
+               'translator_credit': translator_credit }
+    return render_to_response('index.tmpl', context)
+
+def teams(request):
+    all_teams = Team.objects.all()
+    for t in all_teams:
+        t.coordinator_url = "<a href='%s/people/%s'>%s</a>" % (settings.WEBROOT, t.coordinator.id, t.coordinator.name)
+
+    context = {'pageSection': "teams",
+               'webroot': settings.WEBROOT,
+               'teams': utils.sortObjectList(all_teams, 'description') }
+    return render_to_response('list-teams.tmpl', context)
+
+def team(request, langcode):
+    team = Team.objects.get(lang_code=langcode)
+    context = {'pageSection': "teams",
+               'webroot': settings.WEBROOT,
+               'team': team}
+    return render_to_response('team.tmpl', context)
+    
+def languages(request):
+    all_languages = Language.objects.all()
+    context = {'pageSection': "languages",
+               'languages': utils.sortObjectList(all_languages, 'name'),
+               'webroot': settings.WEBROOT }
+    return render_to_response('list-languages.tmpl', context)
+
+def people(request):
+    """ Print a list of all people in database """
+    all_people = Person.objects.all()
+    context = {'pageSection': "teams",
+               'people': all_people,
+               'webroot': settings.WEBROOT }
+    return render_to_response('people.tmpl', context)
+
+def person(request, person_id):
+    """ Print the details of a single person """
+    person = Person.objects.get(id=person_id)
+    maintainer = person.module_set.all()
+    translator = Team.objects.filter(coordinator=person_id)
+    context = {'pageSection':  "teams",
+               'person': person,
+               'printroles': True,
+               'roles': {'maintains':maintainer, 'translates':translator}, 
+               'webroot': settings.WEBROOT }
+    return render_to_response('person.tmpl', context)
+
+def modules(request):
+    all_modules = Module.objects.all()
+    context = {'pageSection':  "module",
+               'webroot': settings.WEBROOT,
+               'modules': utils.sortObjectList(all_modules, 'description') }
+    return render_to_response('list-modules.tmpl', context)
+
+def module(request, module_name):
+    mod = Module.objects.get(name = module_name)
+    mod.translated_name = _(mod.description)
+    context = {'pageSection':  "module",
+               'webroot': settings.WEBROOT,
+               'module': mod,
+               'prof': utils.Profiler() }
+    return render_to_response('module.tmpl', context)
+
+def docimages(request, module_name, potbase, branch_name, langcode):
+    mod = Module.objects.get(name = module_name)
+    stat = Statistics.objects.get(branch__module=mod.id, branch__name=branch_name, domain__name=potbase, language__locale=langcode)
+    context = {'pageSection':  "module",
+               'webroot': settings.WEBROOT,
+               'module': mod,
+               'stat': stat }
+    return render_to_response('module-images.tmpl', context)
+
+def releases(request):
+    all_releases = Release.objects.order_by('status', '-name')
+    context = {'pageSection':  "releases",
+               'webroot': settings.WEBROOT,
+               'releases': all_releases }
+    return render_to_response('list-releases.tmpl', context)
+
+def release(request, release_id):
+    rel = Release.objects.get(id=release_id)
+    context = {'pageSection':  "releases",
+               'webroot': settings.WEBROOT,
+               'release': rel }
+    return render_to_response('release.tmpl', context)
+
+def languagerelease(request, langcode, release_id):
+    rel = Release.objects.get(id=release_id)
+    lang = Language.objects.get(locale=langcode)
+    rel_stats = rel.get_lang_stats(lang)
+    context = {'pageSection':  "languages",
+               'webroot': settings.WEBROOT,
+               'language': lang,
+               'release': rel,
+               'stats': rel_stats}
+    return render_to_response('language-release.tmpl', context)
+
+

Added: branches/djamnedlies/urls.py
==============================================================================
--- (empty file)
+++ branches/djamnedlies/urls.py	Mon Oct 20 21:34:37 2008
@@ -0,0 +1,16 @@
+from django.conf.urls.defaults import *
+
+# Uncomment the next two lines to enable the admin:
+from django.contrib import admin
+admin.autodiscover()
+
+urlpatterns = patterns('',
+    # Uncomment the admin/doc line below and add 'django.contrib.admindocs' 
+    # to INSTALLED_APPS to enable admin documentation:
+    # (r'^admin/doc/', include('django.contrib.admindocs.urls')),
+
+    (r'^admin/(.*)', admin.site.root),
+
+    (r'^stats/', include('djamnedlies.stats.urls')),
+
+)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]