changeset 552:c2627a73e8ec demo

merged with default branch
author Marcin Kuzminski <marcin@python-works.com>
date Mon, 27 Sep 2010 02:18:25 +0200
parents b5e1a66b4aac (current diff) d5efb83590ef (diff)
children d6a88bf3d3e6
files hg_app_daemon hg_app_daemon2
diffstat 18 files changed, 605 insertions(+), 240 deletions(-) [+]
line wrap: on
line diff
--- a/celeryconfig.py	Thu Sep 23 01:24:57 2010 +0200
+++ b/celeryconfig.py	Mon Sep 27 02:18:25 2010 +0200
@@ -36,7 +36,7 @@
 CELERYD_CONCURRENCY = 2
 # CELERYD_LOG_FILE = "celeryd.log"
 CELERYD_LOG_LEVEL = "DEBUG"
-CELERYD_MAX_TASKS_PER_CHILD = 1
+CELERYD_MAX_TASKS_PER_CHILD = 3
 
 #Tasks will never be sent to the queue, but executed locally instead.
 CELERY_ALWAYS_EAGER = False
--- a/hg_app_daemon	Thu Sep 23 01:24:57 2010 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-#!/sbin/runscript
-########################################
-#### THIS IS AN GENTOO INIT.D SCRIPT####
-########################################
-
-APP_NAME="hg_app"
-APP_HOMEDIR="marcink/python_workspace"
-APP_PATH="/home/$APP_HOMEDIR/$APP_NAME"
-
-CONF_NAME="production.ini"
-
-PID_PATH="$APP_PATH/$APP_NAME.pid"
-LOG_PATH="$APP_PATH/$APP_NAME.log"
-
-PYTHON_PATH="/home/$APP_HOMEDIR/v-env"
-
-RUN_AS="marcink"
-
-DAEMON="$PYTHON_PATH/bin/paster"
-
-DAEMON_OPTS="serve --daemon \
---user=$RUN_AS \
---group=$RUN_AS \
---pid-file=$PID_PATH \
---log-file=$LOG_PATH  $APP_PATH/$CONF_NAME"
-
-#extra options
-opts="${opts} restartdelay"
-
-depend() {
-    need nginx
-}
-
-start() {
-    ebegin "Starting $APP_NAME"
-    start-stop-daemon -d $APP_PATH -e PYTHON_EGG_CACHE="/tmp" \
-        --start --quiet \
-        --pidfile $PID_PATH \
-        --user $RUN_AS \
-        --exec $DAEMON -- $DAEMON_OPTS
-    eend $?
-}
-
-stop() {
-    ebegin "Stopping $APP_NAME"
-    start-stop-daemon -d $APP_PATH \
-        --stop --quiet \
-        --pidfile $PID_PATH || echo "$APP_NAME - Not running!"
-    if [ -f $PID_PATH ]; then
-        rm $PID_PATH
-    fi
-    eend $?
-}
-
-restartdelay() {
-    #stop()
-    echo "sleep3"
-    sleep 3
-    
-    #start()
-}
--- a/hg_app_daemon2	Thu Sep 23 01:24:57 2010 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-#!/bin/sh -e
-########################################
-#### THIS IS AN DEBIAN INIT.D SCRIPT####
-########################################
-
-### BEGIN INIT INFO
-# Provides:          hg-app          
-# Required-Start:    $all
-# Required-Stop:     $all
-# Default-Start:     2 3 4 5
-# Default-Stop:      0 1 6
-# Short-Description: starts instance of hg-app
-# Description:       starts instance of hg-app using start-stop-daemon
-### END INIT INFO
-
-APP_NAME="hg_app"
-APP_HOMEDIR="marcink/python_workspace"
-APP_PATH="/home/$APP_HOMEDIR/$APP_NAME"
-
-CONF_NAME="production.ini"
-
-PID_PATH="$APP_PATH/$APP_NAME.pid"
-LOG_PATH="$APP_PATH/$APP_NAME.log"
-
-PYTHON_PATH="/home/$APP_HOMEDIR/v-env"
-
-RUN_AS="marcink"
-
-DAEMON="$PYTHON_PATH/bin/paster"
-
-DAEMON_OPTS="serve --daemon \
---user=$RUN_AS \
---group=$RUN_AS \
---pid-file=$PID_PATH \
---log-file=$LOG_PATH  $APP_PATH/$CONF_NAME"
-
-
-case "$1" in
-  start)
-    echo "Starting $APP_NAME"
-    start-stop-daemon -d $APP_PATH -e PYTHON_EGG_CACHE="/tmp" \
-        --start --quiet \
-        --pidfile $PID_PATH \
-        --user $RUN_AS \
-        --exec $DAEMON -- $DAEMON_OPTS
-    ;;
-  stop)
-    echo "Stopping $APP_NAME"
-    start-stop-daemon -d $APP_PATH \
-        --stop --quiet \
-        --pidfile $PID_PATH || echo "$APP_NAME - Not running!"
-    if [ -f $PID_PATH ]; then
-        rm $PID_PATH
-    fi
-    ;;
-  restart)
-    echo "Restarting $APP_NAME"
-    ### stop ###
-    echo "Stopping $APP_NAME"
-    start-stop-daemon -d $APP_PATH \
-        --stop --quiet \
-        --pidfile $PID_PATH || echo "$APP_NAME - Not running!"
-    if [ -f $PID_PATH ]; then
-        rm $PID_PATH
-    fi
-    ### start ###
-    echo "Starting $APP_NAME"
-    start-stop-daemon -d $APP_PATH -e PYTHON_EGG_CACHE="/tmp" \
-        --start --quiet \
-        --pidfile $PID_PATH \
-        --user $RUN_AS \
-        --exec $DAEMON -- $DAEMON_OPTS
-    ;;
-  *)
-    echo "Usage: $0 {start|stop|restart}"
-    exit 1
-esac
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/init.d/celeryd	Mon Sep 27 02:18:25 2010 +0200
@@ -0,0 +1,224 @@
+#!/bin/sh -e
+# ============================================
+#  celeryd - Starts the Celery worker daemon.
+# ============================================
+#
+# :Usage: /etc/init.d/celeryd {start|stop|force-reload|restart|try-restart|status}
+#
+# :Configuration file: /etc/default/celeryd
+#
+# To configure celeryd you probably need to tell it where to chdir.
+#
+# EXAMPLE CONFIGURATION
+# =====================
+#
+# this is an example configuration for a Python project:
+#
+# /etc/default/celeryd:
+#
+#   # Where to chdir at start.
+#   CELERYD_CHDIR="/opt/Myproject/"
+#
+#   # Extra arguments to celeryd
+#   CELERYD_OPTS="--time-limit 300"
+#
+#   # Name of the celery config module.#
+#   CELERY_CONFIG_MODULE="celeryconfig"
+#
+# EXAMPLE DJANGO CONFIGURATION
+# ============================
+#
+#   # Where the Django project is.
+#   CELERYD_CHDIR="/opt/Project/"
+#
+#   # Name of the projects settings module.
+#   DJANGO_SETTINGS_MODULE="settings"
+#
+#   # Path to celeryd
+#   CELERYD="/opt/Project/manage.py celeryd"
+#
+# AVAILABLE OPTIONS
+# =================
+#
+#   * CELERYD_OPTS
+#       Additional arguments to celeryd, see ``celeryd --help`` for a list.
+#
+#   * CELERYD_CHDIR
+#       Path to chdir at start. Default is to stay in the current directory.
+#
+#   * CELERYD_PIDFILE
+#       Full path to the pidfile. Default is /var/run/celeryd.pid.
+#
+#   * CELERYD_LOGFILE
+#       Full path to the celeryd logfile. Default is /var/log/celeryd.log
+#
+#   * CELERYD_LOG_LEVEL
+#       Log level to use for celeryd. Default is INFO.
+#
+#   * CELERYD
+#       Path to the celeryd program. Default is ``celeryd``.
+#       You can point this to an virtualenv, or even use manage.py for django.
+#
+#   * CELERYD_USER
+#       User to run celeryd as. Default is current user.
+#
+#   * CELERYD_GROUP
+#       Group to run celeryd as. Default is current user.
+
+
+### BEGIN INIT INFO
+# Provides:          celeryd
+# Required-Start:    $network $local_fs $remote_fs
+# Required-Stop:     $network $local_fs $remote_fs
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Short-Description: celery task worker daemon
+### END INIT INFO
+
+set -e
+CELERYD_CHDIR="/opt/Myproject/"
+CELERYD_PID_FILE="/var/run/celeryd.pid"
+CELERYD_LOG_FILE="/var/log/celeryd.log"
+CELERYD_LOG_LEVEL="DEBUG"
+DEFAULT_CELERYD="/home/v-env/celeryd"
+
+# /etc/init.d/ssh: start and stop the celery task worker daemon.
+
+if test -f /etc/default/celeryd; then
+    . /etc/default/celeryd
+fi
+
+CELERYD=${CELERYD:-$DEFAULT_CELERYD}
+
+export CELERY_LOADER
+
+. /lib/lsb/init-functions
+
+CELERYD_OPTS="$CELERYD_OPTS -f $CELERYD_LOG_FILE -l $CELERYD_LOG_LEVEL"
+
+if [ -n "$2" ]; then
+    CELERYD_OPTS="$CELERYD_OPTS $2"
+fi
+
+# Extra start-stop-daemon options, like user/group.
+if [ -n "$CELERYD_USER" ]; then
+    DAEMON_OPTS="$DAEMON_OPTS --chuid $CELERYD_USER"
+fi
+if [ -n "$CELERYD_GROUP" ]; then
+    DAEMON_OPTS="$DAEMON_OPTS --group $CELERYD_GROUP"
+fi
+
+if [ -n "$CELERYD_CHDIR" ]; then
+    DAEMON_OPTS="$DAEMON_OPTS --chdir $CELERYD_CHDIR"
+fi
+
+
+# Are we running from init?
+run_by_init() {
+    ([ "$previous" ] && [ "$runlevel" ]) || [ "$runlevel" = S ]
+}
+
+
+check_dev_null() {
+    if [ ! -c /dev/null ]; then
+    if [ "$1" = log_end_msg ]; then
+        log_end_msg 1 || true
+    fi
+    if ! run_by_init; then
+        log_action_msg "/dev/null is not a character device!"
+    fi
+    exit 1
+    fi
+}
+
+
+export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"
+
+
+stop_worker () {
+    cmd="start-stop-daemon --stop \
+                           --quiet \
+                           $* \
+                           --pidfile $CELERYD_PID_FILE"
+    if $cmd; then
+        log_end_msg 0
+    else
+        log_end_msg 1
+    fi
+}
+
+start_worker () {
+    cmd="start-stop-daemon --start $DAEMON_OPTS \
+                           --quiet \
+                           --oknodo \
+                           --background \
+                           --make-pidfile \
+                           $* \
+                           --pidfile $CELERYD_PID_FILE
+                           --exec $CELERYD -- $CELERYD_OPTS"
+    if $cmd; then
+        log_end_msg 0
+    else
+        log_end_msg 1
+    fi
+}
+
+
+
+case "$1" in
+  start)
+    check_dev_null
+    log_daemon_msg "Starting celery task worker server" "celeryd"
+    start_worker
+    ;;
+  stop)
+    log_daemon_msg "Stopping celery task worker server" "celeryd"
+    stop_worker --oknodo
+    ;;
+
+  reload|force-reload)
+    echo "Use start+stop"
+    ;;
+
+  restart)
+    log_daemon_msg "Restarting celery task worker server" "celeryd"
+    stop_worker --oknodo --retry 30
+    check_dev_null log_end_msg
+    start_worker
+    ;;
+
+  try-restart)
+    log_daemon_msg "Restarting celery task worker server" "celeryd"
+    set +e
+    stop_worker --retry 30
+    RET="$?"
+    set -e
+    case $RET in
+        0)
+        # old daemon stopped
+        check_dev_null log_end_msg
+        start_worker
+        ;;
+        1)
+        # daemon not running
+        log_progress_msg "(not running)"
+        log_end_msg 0
+        ;;
+        *)
+        # failed to stop
+        log_progress_msg "(failed to stop)"
+        log_end_msg 1
+        ;;
+    esac
+    ;;
+
+  status)
+    status_of_proc -p $CELERYD_PID_FILE $CELERYD celeryd && exit 0 || exit $?
+    ;;
+
+  *)
+    log_action_msg "Usage: /etc/init.d/celeryd {start|stop|force-reload|restart|try-restart|status}"
+    exit 1
+esac
+
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/init.d/hg_app_daemon	Mon Sep 27 02:18:25 2010 +0200
@@ -0,0 +1,61 @@
+#!/sbin/runscript
+########################################
+#### THIS IS AN GENTOO INIT.D SCRIPT####
+########################################
+
+APP_NAME="hg_app"
+APP_HOMEDIR="marcink/python_workspace"
+APP_PATH="/home/$APP_HOMEDIR/$APP_NAME"
+
+CONF_NAME="production.ini"
+
+PID_PATH="$APP_PATH/$APP_NAME.pid"
+LOG_PATH="$APP_PATH/$APP_NAME.log"
+
+PYTHON_PATH="/home/$APP_HOMEDIR/v-env"
+
+RUN_AS="marcink"
+
+DAEMON="$PYTHON_PATH/bin/paster"
+
+DAEMON_OPTS="serve --daemon \
+--user=$RUN_AS \
+--group=$RUN_AS \
+--pid-file=$PID_PATH \
+--log-file=$LOG_PATH  $APP_PATH/$CONF_NAME"
+
+#extra options
+opts="${opts} restartdelay"
+
+depend() {
+    need nginx
+}
+
+start() {
+    ebegin "Starting $APP_NAME"
+    start-stop-daemon -d $APP_PATH -e PYTHON_EGG_CACHE="/tmp" \
+        --start --quiet \
+        --pidfile $PID_PATH \
+        --user $RUN_AS \
+        --exec $DAEMON -- $DAEMON_OPTS
+    eend $?
+}
+
+stop() {
+    ebegin "Stopping $APP_NAME"
+    start-stop-daemon -d $APP_PATH \
+        --stop --quiet \
+        --pidfile $PID_PATH || echo "$APP_NAME - Not running!"
+    if [ -f $PID_PATH ]; then
+        rm $PID_PATH
+    fi
+    eend $?
+}
+
+restartdelay() {
+    #stop()
+    echo "sleep3"
+    sleep 3
+    
+    #start()
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/init.d/hg_app_daemon2	Mon Sep 27 02:18:25 2010 +0200
@@ -0,0 +1,77 @@
+#!/bin/sh -e
+########################################
+#### THIS IS AN DEBIAN INIT.D SCRIPT####
+########################################
+
+### BEGIN INIT INFO
+# Provides:          hg-app          
+# Required-Start:    $all
+# Required-Stop:     $all
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Short-Description: starts instance of hg-app
+# Description:       starts instance of hg-app using start-stop-daemon
+### END INIT INFO
+
+APP_NAME="hg_app"
+APP_HOMEDIR="marcink/python_workspace"
+APP_PATH="/home/$APP_HOMEDIR/$APP_NAME"
+
+CONF_NAME="production.ini"
+
+PID_PATH="$APP_PATH/$APP_NAME.pid"
+LOG_PATH="$APP_PATH/$APP_NAME.log"
+
+PYTHON_PATH="/home/$APP_HOMEDIR/v-env"
+
+RUN_AS="marcink"
+
+DAEMON="$PYTHON_PATH/bin/paster"
+
+DAEMON_OPTS="serve --daemon \
+--user=$RUN_AS \
+--group=$RUN_AS \
+--pid-file=$PID_PATH \
+--log-file=$LOG_PATH  $APP_PATH/$CONF_NAME"
+
+
+case "$1" in
+  start)
+    echo "Starting $APP_NAME"
+    start-stop-daemon -d $APP_PATH -e PYTHON_EGG_CACHE="/tmp" \
+        --start --quiet \
+        --pidfile $PID_PATH \
+        --user $RUN_AS \
+        --exec $DAEMON -- $DAEMON_OPTS
+    ;;
+  stop)
+    echo "Stopping $APP_NAME"
+    start-stop-daemon -d $APP_PATH \
+        --stop --quiet \
+        --pidfile $PID_PATH || echo "$APP_NAME - Not running!"
+    if [ -f $PID_PATH ]; then
+        rm $PID_PATH
+    fi
+    ;;
+  restart)
+    echo "Restarting $APP_NAME"
+    ### stop ###
+    echo "Stopping $APP_NAME"
+    start-stop-daemon -d $APP_PATH \
+        --stop --quiet \
+        --pidfile $PID_PATH || echo "$APP_NAME - Not running!"
+    if [ -f $PID_PATH ]; then
+        rm $PID_PATH
+    fi
+    ### start ###
+    echo "Starting $APP_NAME"
+    start-stop-daemon -d $APP_PATH -e PYTHON_EGG_CACHE="/tmp" \
+        --start --quiet \
+        --pidfile $PID_PATH \
+        --user $RUN_AS \
+        --exec $DAEMON -- $DAEMON_OPTS
+    ;;
+  *)
+    echo "Usage: $0 {start|stop|restart}"
+    exit 1
+esac
\ No newline at end of file
--- a/pylons_app/controllers/summary.py	Thu Sep 23 01:24:57 2010 +0200
+++ b/pylons_app/controllers/summary.py	Mon Sep 27 02:18:25 2010 +0200
@@ -35,7 +35,7 @@
 from time import mktime
 import calendar
 import logging
-
+import json
 log = logging.getLogger(__name__)
 
 class SummaryController(BaseController):
@@ -79,18 +79,25 @@
         c.ts_min = ts_min_m
         c.ts_max = ts_max_y
         
-        
         stats = self.sa.query(Statistics)\
             .filter(Statistics.repository == c.repo_info.dbrepo)\
             .scalar()
-
-        if stats:
+        
+        
+        if stats and stats.languages:
+            lang_stats = json.loads(stats.languages)
             c.commit_data = stats.commit_activity
             c.overview_data = stats.commit_activity_combined
+            c.trending_languages = json.dumps(OrderedDict(
+                                       sorted(lang_stats.items(), reverse=True,
+                                            key=lambda k: k[1])[:2]
+                                        )
+                                    )
+            print c.trending_languages
         else:
-            import json
             c.commit_data = json.dumps({})
             c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 0] ])
+            c.trending_languages = json.dumps({})
         
         return render('summary/summary.html')
 
--- a/pylons_app/lib/celerylib/__init__.py	Thu Sep 23 01:24:57 2010 +0200
+++ b/pylons_app/lib/celerylib/__init__.py	Mon Sep 27 02:18:25 2010 +0200
@@ -31,31 +31,24 @@
         return ResultWrapper(task(*args, **kwargs))
 
 
-class LockTask(object):
-    """LockTask decorator"""
-    
-    def __init__(self, func):
-        self.func = func
-        
-    def __call__(self, func):
-        return decorator(self.__wrapper, func)
-    
-    def __wrapper(self, func, *fargs, **fkwargs):
-        params = []
-        params.extend(fargs)
-        params.extend(fkwargs.values())
+def locked_task(func):
+    def __wrapper(func, *fargs, **fkwargs):
+        params = list(fargs)
+        params.extend(['%s-%s' % ar for ar in fkwargs.items()])
+            
         lockkey = 'task_%s' % \
-           md5(str(self.func) + '-' + '-'.join(map(str, params))).hexdigest()
+            md5(str(func.__name__) + '-' + \
+                '-'.join(map(str, params))).hexdigest()
         log.info('running task with lockkey %s', lockkey)
         try:
             l = DaemonLock(lockkey)
-            return func(*fargs, **fkwargs)
+            func(*fargs, **fkwargs)
             l.release()
         except LockHeld:
             log.info('LockHeld')
             return 'Task with key %s already running' % lockkey   
 
-            
+    return decorator(__wrapper, func)      
             
 
         
--- a/pylons_app/lib/celerylib/tasks.py	Thu Sep 23 01:24:57 2010 +0200
+++ b/pylons_app/lib/celerylib/tasks.py	Mon Sep 27 02:18:25 2010 +0200
@@ -1,16 +1,16 @@
 from celery.decorators import task
 from celery.task.sets import subtask
 from celeryconfig import PYLONS_CONFIG as config
+from operator import itemgetter
 from pylons.i18n.translation import _
-from pylons_app.lib.celerylib import run_task, LockTask
+from pylons_app.lib.celerylib import run_task, locked_task
 from pylons_app.lib.helpers import person
 from pylons_app.lib.smtp_mailer import SmtpMailer
 from pylons_app.lib.utils import OrderedDict
-from operator import itemgetter
+from time import mktime
 from vcs.backends.hg import MercurialRepository
-from time import mktime
+import json
 import traceback
-import json
 
 __all__ = ['whoosh_index', 'get_commits_stats',
            'reset_user_password', 'send_email']
@@ -66,28 +66,19 @@
     return settings   
 
 @task
+@locked_task
 def whoosh_index(repo_location, full_index):
     log = whoosh_index.get_logger()
-    from pylons_app.lib.pidlock import DaemonLock
-    from pylons_app.lib.indexers.daemon import WhooshIndexingDaemon, LockHeld
-    try:
-        l = DaemonLock()
-        WhooshIndexingDaemon(repo_location=repo_location)\
-            .run(full_index=full_index)
-        l.release()
-        return 'Done'
-    except LockHeld:
-        log.info('LockHeld')
-        return 'LockHeld'    
-
+    from pylons_app.lib.indexers.daemon import WhooshIndexingDaemon
+    WhooshIndexingDaemon(repo_location=repo_location).run(full_index=full_index)
 
 @task
-@LockTask('get_commits_stats')
+@locked_task
 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
-    author_key_cleaner = lambda k: person(k).replace('"', "") #for js data compatibilty
-        
     from pylons_app.model.db import Statistics, Repository
     log = get_commits_stats.get_logger()
+    author_key_cleaner = lambda k: person(k).replace('"', "") #for js data compatibilty
+    
     commits_by_day_author_aggregate = {}
     commits_by_day_aggregate = {}
     repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
@@ -108,7 +99,7 @@
     if cur_stats:
         last_rev = cur_stats.stat_on_revision
     
-    if last_rev == repo.revisions[-1]:
+    if last_rev == repo.revisions[-1] and len(repo.revisions) > 1:
         #pass silently without any work
         return True
     
@@ -118,6 +109,7 @@
                                         cur_stats.commit_activity_combined))
         commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
     
+    log.debug('starting parsing %s', parse_limit)
     for cnt, rev in enumerate(repo.revisions[last_rev:]):
         last_cs = cs = repo.get_changeset(rev)
         k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
@@ -196,9 +188,16 @@
     stats = cur_stats if cur_stats else Statistics()
     stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
     stats.commit_activity_combined = json.dumps(overview_data)
+
+    log.debug('last revison %s', last_rev)
+    leftovers = len(repo.revisions[last_rev:])
+    log.debug('revisions to parse %s', leftovers)
+    
+    if last_rev == 0 or leftovers < parse_limit:    
+        stats.languages = json.dumps(__get_codes_stats(repo_name))
+        
     stats.repository = dbrepo
     stats.stat_on_revision = last_cs.revision
-    stats.languages = json.dumps({'_TOTAL_':0, '':0})
     
     try:
         sa.add(stats)
@@ -207,8 +206,8 @@
         log.error(traceback.format_exc())
         sa.rollback()
         return False
-    
-    run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
+    if len(repo.revisions) > 1:
+        run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
                             
     return True
 
@@ -268,3 +267,31 @@
         log.error(traceback.format_exc())
         return False
     return True
+
+def __get_codes_stats(repo_name):
+    LANGUAGES_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx', 'aspx', 'asx', 'axd', 'c',
+                    'cfg', 'cfm', 'cpp', 'cs', 'diff', 'do', 'el', 'erl',
+                    'h', 'java', 'js', 'jsp', 'jspx', 'lisp',
+                    'lua', 'm', 'mako', 'ml', 'pas', 'patch', 'php', 'php3',
+                    'php4', 'phtml', 'pm', 'py', 'rb', 'rst', 's', 'sh',
+                    'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt',
+                    'yaws']
+    repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
+    repo = MercurialRepository(repos_path + repo_name)
+
+    code_stats = {}
+    for topnode, dirs, files in repo.walk('/', 'tip'):
+        for f in files:
+            k = f.mimetype
+            if f.extension in LANGUAGES_EXTENSIONS:
+                if code_stats.has_key(k):
+                    code_stats[k] += 1
+                else:
+                    code_stats[k] = 1
+                    
+    return code_stats or {}
+
+
+            
+
+
--- a/pylons_app/lib/helpers.py	Thu Sep 23 01:24:57 2010 +0200
+++ b/pylons_app/lib/helpers.py	Mon Sep 27 02:18:25 2010 +0200
@@ -213,14 +213,23 @@
         return literal('/'.join(url_l))
 
 files_breadcrumbs = _FilesBreadCrumbs()
+class CodeHtmlFormatter(HtmlFormatter):
 
+    def wrap(self, source, outfile):
+        return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
+
+    def _wrap_code(self, source):
+        for cnt, it in enumerate(source, 1):
+            i, t = it
+            t = '<div id="#S-%s">%s</div>' % (cnt, t)
+            yield i, t
 def pygmentize(filenode, **kwargs):
     """
     pygmentize function using pygments
     @param filenode:
     """
     return literal(code_highlight(filenode.content,
-                                  filenode.lexer, HtmlFormatter(**kwargs)))
+                                  filenode.lexer, CodeHtmlFormatter(**kwargs)))
 
 def pygmentize_annotation(filenode, **kwargs):
     """
--- a/pylons_app/lib/hooks.py	Thu Sep 23 01:24:57 2010 +0200
+++ b/pylons_app/lib/hooks.py	Mon Sep 27 02:18:25 2010 +0200
@@ -26,12 +26,13 @@
 import sys
 import os
 from pylons_app.lib import helpers as h
+from pylons_app.model import meta
+from pylons_app.model.db import UserLog, User
 
 def repo_size(ui, repo, hooktype=None, **kwargs):
 
     if hooktype != 'changegroup':
         return False
-    
     size_hg, size_root = 0, 0
     for path, dirs, files in os.walk(repo.root):
         if path.find('.hg') != -1:
@@ -43,6 +44,35 @@
                 
     size_hg_f = h.format_byte_size(size_hg)
     size_root_f = h.format_byte_size(size_root)
-    size_total_f = h.format_byte_size(size_root + size_hg)                            
+    size_total_f = h.format_byte_size(size_root + size_hg)
     sys.stdout.write('Repository size .hg:%s repo:%s total:%s\n' \
                      % (size_hg_f, size_root_f, size_total_f))
+    
+    user_action_mapper(ui, repo, hooktype, **kwargs)
+
+def user_action_mapper(ui, repo, hooktype=None, **kwargs):
+    """
+    Maps user last push action to new changeset id, from mercurial
+    @param ui:
+    @param repo:
+    @param hooktype:
+    """
+    
+    try:
+        sa = meta.Session
+        username = kwargs['url'].split(':')[-1]
+        user_log = sa.query(UserLog)\
+            .filter(UserLog.user == sa.query(User)\
+                                        .filter(User.username == username).one())\
+            .order_by(UserLog.user_log_id.desc()).first()
+            
+        if not user_log.revision:
+            user_log.revision = str(repo['tip'])
+            sa.add(user_log)
+            sa.commit()
+        
+    except Exception, e:
+        sa.rollback()
+        raise
+    finally:
+        meta.Session.remove()    
--- a/pylons_app/lib/middleware/simplehg.py	Thu Sep 23 01:24:57 2010 +0200
+++ b/pylons_app/lib/middleware/simplehg.py	Mon Sep 27 02:18:25 2010 +0200
@@ -121,7 +121,7 @@
             return HTTPNotFound()(environ, start_response)
         try:
             app = wsgiapplication(self.__make_app)
-        except RepoError as e:
+        except RepoError, e:
             if str(e).find('not found') != -1:
                 return HTTPNotFound()(environ, start_response)
         except Exception:
@@ -195,7 +195,7 @@
             sa.commit()
             log.info('Adding user %s, action %s on %s',
                                             user.username, action, repo)
-        except Exception as e:
+        except Exception, e:
             sa.rollback()
             log.error('could not log user action:%s', str(e))
         finally:
--- a/pylons_app/lib/pidlock.py	Thu Sep 23 01:24:57 2010 +0200
+++ b/pylons_app/lib/pidlock.py	Mon Sep 27 02:18:25 2010 +0200
@@ -1,6 +1,8 @@
 import os, time
 import sys
 from warnings import warn
+from multiprocessing.util import Finalize
+import errno
 
 class LockHeld(Exception):pass
 
@@ -9,7 +11,7 @@
     """daemon locking
     USAGE:
     try:
-        l = lock()
+        l = DaemonLock(desc='test lock')
         main()
         l.release()
     except LockHeld:
@@ -27,57 +29,67 @@
         self.held = False
         #run the lock automatically !
         self.lock()
-
-    def __del__(self):
-        if self.held:
+        self._finalize = Finalize(self, DaemonLock._on_finalize,
+                                    args=(self, debug), exitpriority=10)
 
-#            warn("use lock.release instead of del lock",
-#                    category = DeprecationWarning,
-#                    stacklevel = 2)
-
-            # ensure the lock will be removed
-            self.release()
+    @staticmethod
+    def _on_finalize(lock, debug):
+        if lock.held:
+            if debug:
+                print 'leck held finilazing and running lock.release()'
+            lock.release()
 
 
     def lock(self):
-        """
-        locking function, if lock is present it will raise LockHeld exception
+        """locking function, if lock is present it will raise LockHeld exception
         """
         lockname = '%s' % (os.getpid())
-
+        if self.debug:
+            print 'running lock'
         self.trylock()
         self.makelock(lockname, self.pidfile)
         return True
 
     def trylock(self):
         running_pid = False
+        if self.debug:
+            print 'checking for already running process'
         try:
             pidfile = open(self.pidfile, "r")
             pidfile.seek(0)
-            running_pid = pidfile.readline()
+            running_pid = int(pidfile.readline())
+            
+            pidfile.close()
+            
             if self.debug:
                 print 'lock file present running_pid: %s, checking for execution'\
                 % running_pid
             # Now we check the PID from lock file matches to the current
             # process PID
             if running_pid:
-                if os.path.exists("/proc/%s" % running_pid):
-                        print "You already have an instance of the program running"
-                        print "It is running as process %s" % running_pid
-                        raise LockHeld
+                try:
+                    os.kill(running_pid, 0)
+                except OSError, exc:
+                    if exc.errno in (errno.ESRCH, errno.EPERM):
+                        print "Lock File is there but the program is not running"
+                        print "Removing lock file for the: %s" % running_pid                        
+                        self.release()
+                    raise
                 else:
-                        print "Lock File is there but the program is not running"
-                        print "Removing lock file for the: %s" % running_pid
-                        self.release()
+                    print "You already have an instance of the program running"
+                    print "It is running as process %s" % running_pid                    
+                    raise LockHeld()
+                         
         except IOError, e:
             if e.errno != 2:
                 raise
 
-
     def release(self):
+        """releases the pid by removing the pidfile
         """
-        releases the pid by removing the pidfile
-        """
+        if self.debug:
+            print 'trying to release the pidlock'
+            
         if self.callbackfn:
             #execute callback function on release
             if self.debug:
@@ -105,23 +117,3 @@
         pidfile.write(lockname)
         pidfile.close
         self.held = True
-
-
-def main():
-    print 'func is running'
-    cnt = 20
-    while 1:
-        print cnt
-        if cnt == 0:
-            break
-        time.sleep(1)
-        cnt -= 1
-
-
-if __name__ == "__main__":
-    try:
-        l = DaemonLock(desc='test lock')
-        main()
-        l.release()
-    except LockHeld:
-        sys.exit(1)
--- a/pylons_app/model/db.py	Thu Sep 23 01:24:57 2010 +0200
+++ b/pylons_app/model/db.py	Mon Sep 27 02:18:25 2010 +0200
@@ -70,7 +70,7 @@
     repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None)
     action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
     action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
-    
+    revision = Column('revision', TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
     user = relation('User')
     
 class Repository(Base):
--- a/pylons_app/public/css/pygments.css	Thu Sep 23 01:24:57 2010 +0200
+++ b/pylons_app/public/css/pygments.css	Mon Sep 27 02:18:25 2010 +0200
@@ -22,10 +22,13 @@
 	margin-left:25px;
 	font-weight: normal;
 }
+
 div.codeblock .code-body table{
     width: 0 !important;    
 }
-
+div.code-body {
+	background-color: #FFFFFF;
+}
 div.code-body pre .match{
 	background-color: #FAFFA6;
 }
@@ -36,13 +39,10 @@
 	display: block;
 	
 }
-
 div.annotatediv{
 	margin-left:2px;
 	margin-right:4px;
 }
-
-
 .code-highlight {
     padding: 0px;
     margin-top: 5px;
@@ -55,7 +55,6 @@
 }
 .linenos a { text-decoration: none; }
 
-
 .code { display: block; }
 .code-highlight .hll { background-color: #ffffcc }
 .code-highlight .c { color: #408080; font-style: italic } /* Comment */
--- a/pylons_app/public/css/style.css	Thu Sep 23 01:24:57 2010 +0200
+++ b/pylons_app/public/css/style.css	Mon Sep 27 02:18:25 2010 +0200
@@ -2963,6 +2963,28 @@
 /* -----------------------------------------------------------
 	SUMMARY
 ----------------------------------------------------------- */
+.trending_language_tbl, .trending_language_tbl td {
+	margin:  0px !important;
+	padding: 0px !important;
+	border: 0 !important;
+
+}
+.trending_language{
+	-moz-border-radius-bottomright:4px;
+	-moz-border-radius-topright:4px;
+	border-bottom-right-radius: 4px 4px;
+	border-top-right-radius: 4px 4px;
+	background-color:#336699;
+	color:#FFFFFF;
+	display:block;
+	min-width:20px;
+	max-width:400px;
+	padding:3px;
+	text-decoration:none;
+	height: 10px;
+	margin-bottom: 4px;
+	margin-left: 5px;
+}
 
 #clone_url{
 	border: none;
--- a/pylons_app/templates/admin/admin_log.html	Thu Sep 23 01:24:57 2010 +0200
+++ b/pylons_app/templates/admin/admin_log.html	Mon Sep 27 02:18:25 2010 +0200
@@ -13,7 +13,14 @@
 	<tr class="parity${cnt%2}">
 		<td>${h.link_to(l.user.username,h.url('edit_user', id=l.user.user_id))}</td>
 		<td>${h.link_to(l.repository,h.url('summary_home',repo_name=l.repository))}</td>
-		<td>${l.action}</td>
+		<td>
+		% if l.action == 'push' and l.revision:
+		  ${h.link_to('%s - %s' % (l.action,l.revision),
+		  h.url('changeset_home',repo_name=l.repository,revision=l.revision))}
+		%else:
+		  ${l.action}
+		%endif
+		</td>
 		<td>${l.action_date}</td>
 		<td>${l.user_ip}</td>
 	</tr>
--- a/pylons_app/templates/summary/summary.html	Thu Sep 23 01:24:57 2010 +0200
+++ b/pylons_app/templates/summary/summary.html	Mon Sep 27 02:18:25 2010 +0200
@@ -90,7 +90,62 @@
 			      <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
 			  </div>
 			 </div>
-			
+			 
+			 <div class="field">
+			  <div class="label">
+			      <label>${_('Trending languages')}:</label>
+			  </div>
+			  <div class="input-short">
+			    <div id="lang_stats">
+			    
+			    </div> 			  
+			  	<script type="text/javascript">
+			  		var data = ${c.trending_languages|n};
+			  		var total = 0;
+			  		var no_data = true;
+			  		for (k in data){
+			  		    total += data[k];
+			  		    no_data = false;
+			  		} 
+					var tbl = document.createElement('table');
+					tbl.setAttribute('class','trending_language_tbl');
+			  		for (k in data){
+				  		var tr = document.createElement('tr');
+				  		var percentage = Math.round((data[k]/total*100),2);
+						var value = data[k];
+				  		var td1 = document.createElement('td');
+				  		td1.width=150;
+				  		var trending_language_label = document.createElement('div');
+				  		trending_language_label.innerHTML = k;
+				  		td1.appendChild(trending_language_label);
+
+				  		var td2 = document.createElement('td');
+			  		    var trending_language = document.createElement('div');
+			  		    trending_language.title = k;
+			  		    trending_language.innerHTML = "<b>"+value+" ${_('files')} - "+percentage+" %</b>";
+			  		    trending_language.setAttribute("class", 'trending_language');
+			  		    trending_language.style.width=percentage+"%";
+						td2.appendChild(trending_language);
+						
+						tr.appendChild(td1);
+						tr.appendChild(td2);
+			  		    tbl.appendChild(tr);
+ 				  		//YAHOO.util.Dom.get('lang_stats').appendChild(trending_language_label);
+			  		    
+			  		}
+			  		if(no_data){
+			  			var tr = document.createElement('tr');
+			  			var td1 = document.createElement('td');
+			  			td1.innerHTML = "${_('No data loaded yet...')}";
+			  			tr.appendChild(td1);
+			  			tbl.appendChild(tr);
+					}
+			  		YAHOO.util.Dom.get('lang_stats').appendChild(tbl);
+			  	</script>
+ 
+			  </div>
+			 </div>
+			 			
 			 <div class="field">
 			  <div class="label">
 			      <label>${_('Download')}:</label>