<commit_before># -*- coding: utf-8 -*-
# Copyright 2011 Lucas Clemente Vella
# Software under Affero GPL license, see LICENSE.txt

import itertools
import datetime
import urllib2
import json
import ssl
from diggems import settings
from wsgiref.handlers import format_date_time
from time import mktime

from django.shortcuts import get_object_or_404, render_to_response
from django.http import *
from django.db import IntegrityError, transaction
from django.db.models import Q
from django.template import Context, RequestContext, loader, TemplateDoesNotExist
from django.utils.html import escape
from django.utils.translation import ugettext as _, pgettext
from django.core.exceptions import ObjectDoesNotExist
from game_helpers import *
from models import *
from https_conn import https_opener
from django.utils.translation import to_locale, get_language

def render_with_extra(template_name, user, data={}, status=200):
    t = loader.get_template(template_name)
    c = Context(data)

    try:
        win_ratio = (float(user.games_won) / user.games_finished) * 100.0
    except ZeroDivisionError:
        win_ratio = None

    extra = {'FB_APP_ID': FB_APP_ID,
             'fb': user.facebook,
             'stats': {'score': user.total_score,
                       'victories': user.games_won,
                       'win_ratio': win_ratio}
            }
    c.update(extra)
    return HttpResponse(t.render(c), status=status)

def fb_channel(request):
    resp = HttpResponse('<script src="//connect.facebook.net/{}/all.js"></script>'.format(pgettext("Facebook", "en_US")))
    secs = 60*60*24*365
    resp['Pragma'] = 'public'
    resp['Cache-Control'] = 'max-age=' + str(secs)
    far_future = (datetime.datetime.now() + datetime.timedelta(seconds=secs))
    resp['Expires'] = format_date_time(mktime(far_future.timetuple()))
    return resp

@transaction.commit_on_success
def fb_login(request):
    token = request.POST.get('token')
    if not token:
        return HttpResponseBadRequest()

    try:
        # TODO: this ideally must be done asyncronuosly...
        res = https_opener.open('https://graph.facebook.com/me?access_token=' + token)
        fb_user = json.load(res)
        res.close()
    except ssl.SSLError:
        # TODO: Log this error? What to do when Facebook
        # connection has been compromised?
        return HttpResponseServerError()

    try:
        fb = FacebookCache.objects.get(uid=fb_user['id'])
    except FacebookCache.DoesNotExist:
        fb = FacebookCache(uid=fb_user['id'])

    fb.name = fb_user['name']
    fb.save()

    old_user_id = request.session.get('user_id')
    try:
        profile = UserProfile.objects.get(facebook=fb)
        if old_user_id and old_user_id != profile.id:
            try:
                old_profile = UserProfile.objects.get(pk=old_user_id)
                if not old_profile.user and not old_profile.facebook:
                    profile.merge(old_profile)
            except UserProfile.DoesNotExist:
                pass
    except UserProfile.DoesNotExist:
        # First time login with Facebook
        try:
            profile = UserProfile.objects.get(pk=old_user_id)
            if not profile.user and not profile.facebook:
                profile.facebook = fb
            else:
                raise Exception()
        except:
            profile = UserProfile(id=gen_token(), facebook=fb)
    profile.save()

    request.session['user_id'] = profile.id

    t = loader.get_template('auth_fb.json')
    c = Context({'fb': fb})
    return HttpResponse(t.render(c), mimetype='application/json')

@transaction.commit_on_success
def fb_logout(request):
    profile = UserProfile.get(request)
    if profile.user or profile.facebook:
        profile = UserProfile(id=gen_token())
        profile.save()
        request.session['user_id'] = profile.id

    return HttpResponse()

def index(request):
    profile = UserProfile.get(request)

    playing_now = Game.objects.filter(Q(p1__user=profile) | Q(p2__user=profile)).exclude(state__gte=3)

    chosen = Game.objects.filter(state__exact=0, token__isnull=True).exclude(p1__user__exact=profile).order_by('?')[:5]
    new_games = []
    for game in chosen:
        info = {'id': game.id}
        player = game.p1.user
        if player.facebook:
            info['op_name'] = player.facebook.name
            info['op_picture'] = ('https://graph.facebook.com/{}/picture'.format(player.facebook.uid))
        else:
            info['op_name'] = _('anonymous player')

        new_games.append(info)

    context = {'your_games': playing_now, 'new_games': new_games, 'like_url': settings.FB_LIKE_URL}
    return render_with_extra('index.html', profile, context)

@transaction.commit_on_success
def new_game(request):
    if request.method != 'POST':
        c = Context({'url': '/new_game/'})
        return render_to_response('post_redirect.html', c)

    profile = UserProfile.get(request)

    mine = [[0] * 16 for i in xrange(16)]

    indexes = list(itertools.product(xrange(16), repeat=2))
    gems = true_random.sample(indexes, 51)

    for (m, n) in gems:
        mine[m][n] = 9

    for m, n in indexes:
        if mine[m][n] == 0:
            def inc_count(x, y):
                if mine[x][y] == 9:
                    mine[m][n] += 1
            for_each_surrounding(m, n, inc_count)

    p1 = Player(user=profile)
    p1.save()

    game = Game()
    game.mine = mine_encode(mine)
    if request.REQUEST.get('private', default=False):
        game.token = gen_token()
    game.channel = gen_token()
    game.p1 = p1
    game.save()
    create_channel(game.channel)

    return HttpResponseRedirect('/game/' + str(game.id))

@transaction.commit_on_success
def join_game(request, game_id):
    profile = UserProfile.get(request)

    # If game is too old, render 404 game error screen
    try:
        game = Game.objects.get(pk=int(game_id))
    except ObjectDoesNotExist:
        return render_with_extra('game404.html', profile, status=404)

    # If already playing this game, redirect to game screen
    if game.what_player(profile):
        return HttpResponseRedirect('/game/' + str(game.id))

    # If user cannot start this game, then 403
    token = request.REQUEST.get('token')
    if game.state != 0 or (game.token and
                           token != game.token):
        return render_with_extra('game403.html', profile, status=403)

    # If we got here via GET, return a page that will make the client/user
    # retry via POST. Done so that Facebook and other robots do not join
    # the game in place of a real user.
    if request.method != 'POST':
        url = '/game/{}/join/'.format(game_id)
        if token:
            url = '{}?token={}'.format(url, token)
        c = Context({'url': url})
        return render_to_response('post_redirect.html', c)

    p2 = Player(user=profile)
    p2.save()

    game.p2 = p2
    game.state = 1
    game.save()

    outdata = map(unicode, [game.seq_num, game.state])
    fb = profile.facebook
    if fb:
        outdata += [fb.uid, escape(fb.name)]

    post_update(game.channel, u'\n'.join(outdata))
    return HttpResponseRedirect('/game/' + game_id)

@transaction.commit_on_success
def abort_game(request, game_id):
    if request.method != 'POST':
        return HttpResponseNotAllowed(['POST'])

    game = get_object_or_404(Game, pk=game_id)
    if game.state == 0:
        profile = UserProfile.get(request)
        pdata = game.what_player(profile)
        if pdata:
            pdata[1].delete()
            game.delete()
            return HttpResponseRedirect('/')

    return HttpResponseForbidden()

@transaction.commit_on_success
def claim_game(request, game_id):
    if request.method != 'POST':
        return HttpResponseNotAllowed(['POST'])
    game = get_object_or_404(Game, pk=game_id)
    if game.state not in (1,2):
        return HttpResponseForbidden()

    profile = UserProfile.get(request)
    pdata = game.what_player(profile)
    if pdata:
        my_number, me = pdata
    else:
        return HttpResponseForbidden()
    if my_number == game.state or game.timeout_diff() > 0:
        return HttpResponseForbidden()

    term = request.POST.get('terminate') == 'y'
    if term:
        points = game.mine.count(tile_encode(19)) + game.mine.count(tile_encode(20))
        profile.total_score += points
        profile.save()

        game.state = my_number + 4

    else:
        game.state = my_number;

    game.save()
    transaction.commit()

    result = '\n'.join(map(str, (game.seq_num, game.state)))
    post_update(game.channel, result)

    if term:
        publish_score(me)

    return HttpResponse()

@transaction.commit_on_success
def game(request, game_id):
    # TODO: maybe control who can watch a game
    profile = UserProfile.get(request)
    #game = get_object_or_404(Game, pk=game_id)
    try:
        game = Game.objects.get(pk=int(game_id))
    except ObjectDoesNotExist:
        return render_with_extra('game404.html', profile, status=404)

    data = {'state': game.state,
            'game_id': game_id,
            'seq_num': game.seq_num,
            'last_change': format_date_time(mktime(datetime.datetime.now().timetuple())),
            'channel': game.channel,
            'p1_last_move': game.p1.last_move}

    if(game.p1.user.facebook):
        data['p1_info'] = game.p1.user.facebook.pub_info()

    if(game.p2):
        data['p2_last_move'] = game.p2.last_move
        if(game.p2.user.facebook):
            data['p2_info'] = game.p2.user.facebook.pub_info()
        if (game.state <= 2):
            data['time_left'] = max(0, game.timeout_diff())

    pdata = game.what_player(profile)
    if pdata:
        my_number, me = pdata
        data['tnt_used'] = not me.has_tnt
        data['player'] = my_number
        me.save()

        if game.state == 0:
            protocol = 'https' if request.is_secure() else 'http'
            data['base_url'] = '{}://{}'.format(protocol, request.get_host())

    if game.state == 0 and game.token: # Uninitialized private game
        data['token'] = game.token
    else:
        masked = mine_mask(game.mine, game.state in (2, 3))
        if masked.count('?') != 256:
            data['mine'] = masked

    return render_with_extra('game.html', profile, data)

@transaction.commit_on_success
def move(request, game_id):
    if request.method != 'POST':
        return HttpResponseForbidden()

    game = get_object_or_404(Game, pk=game_id)

    pdata = game.what_player(UserProfile.get(request))
    if not pdata or pdata[0] != game.state:
        return HttpResponseForbidden()

    player, me = pdata

    try:
        m = int(request.REQUEST['m'])
        n = int(request.REQUEST['n'])
        if not (0 <= m <= 15 and 0 <= n <= 15):
            raise ValueError
    except:
        return HttpResponseBadRequest()

    mine = mine_decode(game.mine)

    to_reveal = [(m, n)]
    tnt_used = False

    if request.REQUEST.get('tnt') == 'y':
        if not me.has_tnt:
            return HttpResponseBadRequest()
        me.has_tnt = False
        to_reveal = itertools.product(xrange(max(m-2, 0),
                                             min(m+3, 16)),
                                      xrange(max(n-2, 0),
                                             min(n+3, 16)))
        tnt_used = True

    revealed = []
    def reveal(m, n):
        if mine[m][n] >= 10:
            return

        old = mine[m][n]
        mine[m][n] += 10
        if mine[m][n] == 19 and player == 2:
            mine[m][n] = 20
        revealed.append((m, n, tile_mask(mine[m][n])))
        if old == 0:
            for_each_surrounding(m, n, reveal)

    for x, y in to_reveal:
        reveal(x, y)

    if not revealed:
        return HttpResponseBadRequest()

    if mine[m][n] <= 18 or tnt_used:
        game.state = (game.state % 2) + 1

    new_mine = mine_encode(mine)
    points = [new_mine.count(tile_encode(19)), new_mine.count(tile_encode(20))]

    if points[0] >= 26 or points[1] >= 26:
        game.state = player + 2

    coded_move = '%s%x%x' % ('b' if tnt_used else 'd', m, n)
    me.last_move = coded_move
    game.mine = new_mine
    game.save()
    me.save()

    if game.state >= 3: # Game is over
        remaining = 51 - points[0] - points[1]
        points[0 if points[0] > points[1] else 1] += remaining

        for user, idx in ((game.p1.user, 0), (game.p2.user, 1)):
            user.games_finished = F('games_finished') + 1
            user.total_score = F('total_score') + points[idx]
            if game.state == (idx + 3):
                user.games_won = F('games_won') + 1

            user.save()

        for m, n in itertools.product(xrange(0, 16), xrange(0, 16)):
             if mine[m][n] == 9:
                 revealed.append((m, n, 'x'))

    result = itertools.chain((str(game.seq_num), str(game.state), str(player), coded_move), ['%d,%d:%c' % x for x in revealed])
    result = '\n'.join(result)

    # Everything is OK until now, so commit DB transaction
    transaction.commit()

    # Since updating Facebook with score may be slow, we post
    # the update to the user first...
    post_update(game.channel, result)

    # ... and then publish the scores on FB, if game is over.
    # (TODO: If only this could be done asyncronously...)
    if game.state >= 3:
        publish_score(game.p1.user)
        publish_score(game.p2.user)

    return HttpResponse()

def donate(request):
    profile = UserProfile.get(request)
    return render_with_extra('donate.html', profile, {'like_url': settings.FB_LIKE_URL})

def info(request, page):
    actual_locale = get_language()
    if page not in info.existing_pages:
        raise Http404
    for locale in (actual_locale, 'en'):
        try:
            return render_with_extra('{}/{}.html'.format(locale, page), UserProfile.get(request))
        except TemplateDoesNotExist:
            continue
info.existing_pages = frozenset(('about', 'howtoplay', 'sourcecode', 'contact', 'privacy', 'terms'))
<commit_msg>Fixing worst bug, diamonds are no longer shown to players.<commit_after># -*- coding: utf-8 -*-
# Copyright 2011 Lucas Clemente Vella
# Software under Affero GPL license, see LICENSE.txt

import itertools
import datetime
import urllib2
import json
import ssl
from diggems import settings
from wsgiref.handlers import format_date_time
from time import mktime

from django.shortcuts import get_object_or_404, render_to_response
from django.http import *
from django.db import IntegrityError, transaction
from django.db.models import Q
from django.template import Context, RequestContext, loader, TemplateDoesNotExist
from django.utils.html import escape
from django.utils.translation import ugettext as _, pgettext
from django.core.exceptions import ObjectDoesNotExist
from game_helpers import *
from models import *
from https_conn import https_opener
from django.utils.translation import to_locale, get_language

def render_with_extra(template_name, user, data={}, status=200):
    t = loader.get_template(template_name)
    c = Context(data)

    try:
        win_ratio = (float(user.games_won) / user.games_finished) * 100.0
    except ZeroDivisionError:
        win_ratio = None

    extra = {'FB_APP_ID': FB_APP_ID,
             'fb': user.facebook,
             'stats': {'score': user.total_score,
                       'victories': user.games_won,
                       'win_ratio': win_ratio}
            }
    c.update(extra)
    return HttpResponse(t.render(c), status=status)

def fb_channel(request):
    resp = HttpResponse('<script src="//connect.facebook.net/{}/all.js"></script>'.format(pgettext("Facebook", "en_US")))
    secs = 60*60*24*365
    resp['Pragma'] = 'public'
    resp['Cache-Control'] = 'max-age=' + str(secs)
    far_future = (datetime.datetime.now() + datetime.timedelta(seconds=secs))
    resp['Expires'] = format_date_time(mktime(far_future.timetuple()))
    return resp

@transaction.commit_on_success
def fb_login(request):
    token = request.POST.get('token')
    if not token:
        return HttpResponseBadRequest()

    try:
        # TODO: this ideally must be done asyncronuosly...
        res = https_opener.open('https://graph.facebook.com/me?access_token=' + token)
        fb_user = json.load(res)
        res.close()
    except ssl.SSLError:
        # TODO: Log this error? What to do when Facebook
        # connection has been compromised?
        return HttpResponseServerError()

    try:
        fb = FacebookCache.objects.get(uid=fb_user['id'])
    except FacebookCache.DoesNotExist:
        fb = FacebookCache(uid=fb_user['id'])

    fb.name = fb_user['name']
    fb.save()

    old_user_id = request.session.get('user_id')
    try:
        profile = UserProfile.objects.get(facebook=fb)
        if old_user_id and old_user_id != profile.id:
            try:
                old_profile = UserProfile.objects.get(pk=old_user_id)
                if not old_profile.user and not old_profile.facebook:
                    profile.merge(old_profile)
            except UserProfile.DoesNotExist:
                pass
    except UserProfile.DoesNotExist:
        # First time login with Facebook
        try:
            profile = UserProfile.objects.get(pk=old_user_id)
            if not profile.user and not profile.facebook:
                profile.facebook = fb
            else:
                raise Exception()
        except:
            profile = UserProfile(id=gen_token(), facebook=fb)
    profile.save()

    request.session['user_id'] = profile.id

    t = loader.get_template('auth_fb.json')
    c = Context({'fb': fb})
    return HttpResponse(t.render(c), mimetype='application/json')

@transaction.commit_on_success
def fb_logout(request):
    profile = UserProfile.get(request)
    if profile.user or profile.facebook:
        profile = UserProfile(id=gen_token())
        profile.save()
        request.session['user_id'] = profile.id

    return HttpResponse()

def index(request):
    profile = UserProfile.get(request)

    playing_now = Game.objects.filter(Q(p1__user=profile) | Q(p2__user=profile)).exclude(state__gte=3)

    chosen = Game.objects.filter(state__exact=0, token__isnull=True).exclude(p1__user__exact=profile).order_by('?')[:5]
    new_games = []
    for game in chosen:
        info = {'id': game.id}
        player = game.p1.user
        if player.facebook:
            info['op_name'] = player.facebook.name
            info['op_picture'] = ('https://graph.facebook.com/{}/picture'.format(player.facebook.uid))
        else:
            info['op_name'] = _('anonymous player')

        new_games.append(info)

    context = {'your_games': playing_now, 'new_games': new_games, 'like_url': settings.FB_LIKE_URL}
    return render_with_extra('index.html', profile, context)

@transaction.commit_on_success
def new_game(request):
    if request.method != 'POST':
        c = Context({'url': '/new_game/'})
        return render_to_response('post_redirect.html', c)

    profile = UserProfile.get(request)

    mine = [[0] * 16 for i in xrange(16)]

    indexes = list(itertools.product(xrange(16), repeat=2))
    gems = true_random.sample(indexes, 51)

    for (m, n) in gems:
        mine[m][n] = 9

    for m, n in indexes:
        if mine[m][n] == 0:
            def inc_count(x, y):
                if mine[x][y] == 9:
                    mine[m][n] += 1
            for_each_surrounding(m, n, inc_count)

    p1 = Player(user=profile)
    p1.save()

    game = Game()
    game.mine = mine_encode(mine)
    if request.REQUEST.get('private', default=False):
        game.token = gen_token()
    game.channel = gen_token()
    game.p1 = p1
    game.save()
    create_channel(game.channel)

    return HttpResponseRedirect('/game/' + str(game.id))

@transaction.commit_on_success
def join_game(request, game_id):
    profile = UserProfile.get(request)

    # If game is too old, render 404 game error screen
    try:
        game = Game.objects.get(pk=int(game_id))
    except ObjectDoesNotExist:
        return render_with_extra('game404.html', profile, status=404)

    # If already playing this game, redirect to game screen
    if game.what_player(profile):
        return HttpResponseRedirect('/game/' + str(game.id))

    # If user cannot start this game, then 403
    token = request.REQUEST.get('token')
    if game.state != 0 or (game.token and
                           token != game.token):
        return render_with_extra('game403.html', profile, status=403)

    # If we got here via GET, return a page that will make the client/user
    # retry via POST. Done so that Facebook and other robots do not join
    # the game in place of a real user.
    if request.method != 'POST':
        url = '/game/{}/join/'.format(game_id)
        if token:
            url = '{}?token={}'.format(url, token)
        c = Context({'url': url})
        return render_to_response('post_redirect.html', c)

    p2 = Player(user=profile)
    p2.save()

    game.p2 = p2
    game.state = 1
    game.save()

    outdata = map(unicode, [game.seq_num, game.state])
    fb = profile.facebook
    if fb:
        outdata += [fb.uid, escape(fb.name)]

    post_update(game.channel, u'\n'.join(outdata))
    return HttpResponseRedirect('/game/' + game_id)

@transaction.commit_on_success
def abort_game(request, game_id):
    if request.method != 'POST':
        return HttpResponseNotAllowed(['POST'])

    game = get_object_or_404(Game, pk=game_id)
    if game.state == 0:
        profile = UserProfile.get(request)
        pdata = game.what_player(profile)
        if pdata:
            pdata[1].delete()
            game.delete()
            return HttpResponseRedirect('/')

    return HttpResponseForbidden()

@transaction.commit_on_success
def claim_game(request, game_id):
    if request.method != 'POST':
        return HttpResponseNotAllowed(['POST'])
    game = get_object_or_404(Game, pk=game_id)
    if game.state not in (1,2):
        return HttpResponseForbidden()

    profile = UserProfile.get(request)
    pdata = game.what_player(profile)
    if pdata:
        my_number, me = pdata
    else:
        return HttpResponseForbidden()
    if my_number == game.state or game.timeout_diff() > 0:
        return HttpResponseForbidden()

    term = request.POST.get('terminate') == 'y'
    if term:
        points = game.mine.count(tile_encode(19)) + game.mine.count(tile_encode(20))
        profile.total_score += points
        profile.save()

        game.state = my_number + 4

    else:
        game.state = my_number;

    game.save()
    transaction.commit()

    result = '\n'.join(map(str, (game.seq_num, game.state)))
    post_update(game.channel, result)

    if term:
        publish_score(me)

    return HttpResponse()

@transaction.commit_on_success
def game(request, game_id):
    # TODO: maybe control who can watch a game
    profile = UserProfile.get(request)
    #game = get_object_or_404(Game, pk=game_id)
    try:
        game = Game.objects.get(pk=int(game_id))
    except ObjectDoesNotExist:
        return render_with_extra('game404.html', profile, status=404)

    data = {'state': game.state,
            'game_id': game_id,
            'seq_num': game.seq_num,
            'last_change': format_date_time(mktime(datetime.datetime.now().timetuple())),
            'channel': game.channel,
            'p1_last_move': game.p1.last_move}

    if(game.p1.user.facebook):
        data['p1_info'] = game.p1.user.facebook.pub_info()

    if(game.p2):
        data['p2_last_move'] = game.p2.last_move
        if(game.p2.user.facebook):
            data['p2_info'] = game.p2.user.facebook.pub_info()
        if (game.state <= 2):
            data['time_left'] = max(0, game.timeout_diff())

    pdata = game.what_player(profile)
    if pdata:
        my_number, me = pdata
        data['tnt_used'] = not me.has_tnt
        data['player'] = my_number
        me.save()

        if game.state == 0:
            protocol = 'https' if request.is_secure() else 'http'
            data['base_url'] = '{}://{}'.format(protocol, request.get_host())

    if game.state == 0 and game.token: # Uninitialized private game
        data['token'] = game.token
    else:
        masked = mine_mask(game.mine, game.state in (3, 4))
        if masked.count('?') != 256:
            data['mine'] = masked

    return render_with_extra('game.html', profile, data)

@transaction.commit_on_success
def move(request, game_id):
    if request.method != 'POST':
        return HttpResponseForbidden()

    game = get_object_or_404(Game, pk=game_id)

    pdata = game.what_player(UserProfile.get(request))
    if not pdata or pdata[0] != game.state:
        return HttpResponseForbidden()

    player, me = pdata

    try:
        m = int(request.REQUEST['m'])
        n = int(request.REQUEST['n'])
        if not (0 <= m <= 15 and 0 <= n <= 15):
            raise ValueError
    except:
        return HttpResponseBadRequest()

    mine = mine_decode(game.mine)

    to_reveal = [(m, n)]
    tnt_used = False

    if request.REQUEST.get('tnt') == 'y':
        if not me.has_tnt:
            return HttpResponseBadRequest()
        me.has_tnt = False
        to_reveal = itertools.product(xrange(max(m-2, 0),
                                             min(m+3, 16)),
                                      xrange(max(n-2, 0),
                                             min(n+3, 16)))
        tnt_used = True

    revealed = []
    def reveal(m, n):
        if mine[m][n] >= 10:
            return

        old = mine[m][n]
        mine[m][n] += 10
        if mine[m][n] == 19 and player == 2:
            mine[m][n] = 20
        revealed.append((m, n, tile_mask(mine[m][n])))
        if old == 0:
            for_each_surrounding(m, n, reveal)

    for x, y in to_reveal:
        reveal(x, y)

    if not revealed:
        return HttpResponseBadRequest()

    if mine[m][n] <= 18 or tnt_used:
        game.state = (game.state % 2) + 1

    new_mine = mine_encode(mine)
    points = [new_mine.count(tile_encode(19)), new_mine.count(tile_encode(20))]

    if points[0] >= 26 or points[1] >= 26:
        game.state = player + 2

    coded_move = '%s%x%x' % ('b' if tnt_used else 'd', m, n)
    me.last_move = coded_move
    game.mine = new_mine
    game.save()
    me.save()

    if game.state >= 3: # Game is over
        remaining = 51 - points[0] - points[1]
        points[0 if points[0] > points[1] else 1] += remaining

        for user, idx in ((game.p1.user, 0), (game.p2.user, 1)):
            user.games_finished = F('games_finished') + 1
            user.total_score = F('total_score') + points[idx]
            if game.state == (idx + 3):
                user.games_won = F('games_won') + 1

            user.save()

        for m, n in itertools.product(xrange(0, 16), xrange(0, 16)):
             if mine[m][n] == 9:
                 revealed.append((m, n, 'x'))

    result = itertools.chain((str(game.seq_num), str(game.state), str(player), coded_move), ['%d,%d:%c' % x for x in revealed])
    result = '\n'.join(result)

    # Everything is OK until now, so commit DB transaction
    transaction.commit()

    # Since updating Facebook with score may be slow, we post
    # the update to the user first...
    post_update(game.channel, result)

    # ... and then publish the scores on FB, if game is over.
    # (TODO: If only this could be done asyncronously...)
    if game.state >= 3:
        publish_score(game.p1.user)
        publish_score(game.p2.user)

    return HttpResponse()

def donate(request):
    profile = UserProfile.get(request)
    return render_with_extra('donate.html', profile, {'like_url': settings.FB_LIKE_URL})

def info(request, page):
    actual_locale = get_language()
    if page not in info.existing_pages:
        raise Http404
    for locale in (actual_locale, 'en'):
        try:
            return render_with_extra('{}/{}.html'.format(locale, page), UserProfile.get(request))
        except TemplateDoesNotExist:
            continue
info.existing_pages = frozenset(('about', 'howtoplay', 'sourcecode', 'contact', 'privacy', 'terms'))
<|endoftext|>


<commit_before>"""Game views"""
import json
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect, HttpResponse
from django.urls import reverse
from django.core import serializers
from django.contrib.auth import logout
from django.contrib.auth.decorators import login_required
from django.views import View

from .models import Location, User
from .models import Safezone
from .models import Announcement
from .models import Profile

def index(request):
    """Homepage view"""
    context = {'text': 'Welcome to our game'}
    return render(request, 'game/index.html', context)

def logout_successful(request):
    context = {'text': 'Logout successful'}
    return render(request, 'game/logout_successful.html', context)
"""
def ajax_user_search( request ):

    if request.is_ajax():
        q = request.GET.get( 'q' )
        if q is not None:
            results = User.objects.filter(username=q)

            return render_to_response( 'users.html', { 'results': results, },
                                       context_instance = RequestContext( request ) )
"""
def users(request):
    """User search home"""
    context = {'text': 'Find a user:'}
    return render(request, 'game/users.html', context)

def user_search(request):
    """User search request"""
    query = request.GET.get('user_id')
    if query:
        results = User.objects.filter(username__contains=query)
        if results == None:
            results = 'No users found.'
        rates = []
        for i in results:
            curr_user = i
            rates.append(i.profile.success_rate())
        results = zip(results, rates)
        context = {'text': 'Find a user:', 'searched': query,
        'results': results}
    else:
        context = {'searched': 'No input detected.'}
    return render(request, 'game/search_results.html', context)

@login_required()
def user_detail(request):
    """User profile view"""
    current_user = request.user
    context = {'username': current_user.username,
               'total_matches': int(current_user.profile.total_matches()),
               'matches_won': int(current_user.profile.matches_won),
               'win_rate': round(current_user.profile.success_rate(), 2)
    }
    return render(request, 'game/user_detail.html', context)

def leaderboard(request):
    """Leaderboard view"""

    context = {'text': 'Leaderboard goes here'}
    return render(request, 'game/leaderboard.html', context)

def play(request):
    """Game view"""
    # context = {'text': 'Leaderboard goes here'}
    return render(request, 'game/play.html')

def location_json(request):
    """Retrieving location information"""
    location_list = Location.objects.all()
    location_json_list = serializers.serialize('json', location_list)
    # Convert JSON to python dict
    location_object = json.loads(location_json_list)

    # Create a new object and make the keys be the location names so it's easier to search later
    output_object = {}
    for location in location_object:
        location_text = location['fields']['location_text']
        output_object[location_text] = location
    return HttpResponse(json.dumps(output_object), content_type='application/javascript')

def safezone_json(request):
    """Retrieving safezone locations"""
    safezone_list = Safezone.objects.all()
    safezone_json_list = serializers.serialize('json', safezone_list)
    # Convert JSON to python dict
    safezone_object = json.loads(safezone_json_list)

    # Create a new object and make the keys be the location names so it's easier to search later
    output_object = {}
    for location in safezone_object:
        location_text = location['fields']['location_text']
        output_object[location_text] = location
    return HttpResponse(json.dumps(output_object), content_type='application/javascript')

def win(request, location_name):
    """Saving matches won to database"""
    location = get_object_or_404(Location, location_text=location_name)
    location.matches_won += 1
    location.save()

    if request.user.is_authenticated:
        current_user = request.user
        current_user.profile.matches_won += 1
        current_user.profile.save()

    return HttpResponse("matches_won increased by 1")

def lose(request, location_name):
    """Saving matches lost to database"""
    location = get_object_or_404(Location, location_text=location_name)
    location.matches_lost += 1
    location.save()

    if request.user.is_authenticated:
        current_user = request.user
        current_user.profile.matches_lost += 1
        current_user.profile.save()

    return HttpResponse("matches_lost increased by 1")

def announcement_json(request):
    announcement_list = Announcement.objects.all()
    announcementJSON = serializers.serialize('json', announcement_list)
    # Convert JSON to python dict
    announcementObject = json.loads(announcementJSON)
    outputObject = {}
    for announcement in announcementObject:
        announcement_text = announcement['fields']['announcement_text']
        outputObject[announcement_text] = announcement
    return HttpResponse(json.dumps(outputObject), content_type='application/javascript')

def profile_json(request):
    pass
<commit_msg>leaderboard returns top 5 users<commit_after>"""Game views"""
import json
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect, HttpResponse
from django.urls import reverse
from django.core import serializers
from django.contrib.auth import logout
from django.contrib.auth.decorators import login_required
from django.views import View

from .models import Location, User
from .models import Safezone
from .models import Announcement
from .models import Profile

def index(request):
    """Homepage view"""
    context = {'text': 'Welcome to our game'}
    return render(request, 'game/index.html', context)

def logout_successful(request):
    context = {'text': 'Logout successful'}
    return render(request, 'game/logout_successful.html', context)

def users(request):
    """User search home"""
    context = {'text': 'Find a user:'}
    return render(request, 'game/users.html', context)

def user_search(request):
    """User search request"""
    query = request.GET.get('user_id')
    if query:
        results = User.objects.filter(username__contains=query)
        if results == None:
            results = 'No users found.'
        rates = []
        for i in results:
            curr_user = i
            rates.append(i.profile.success_rate())
        results = zip(results, rates)
        context = {'text': 'Find a user:', 'searched': query,
        'results': results}
    else:
        context = {'searched': 'No input detected.'}
    return render(request, 'game/search_results.html', context)

@login_required()
def user_detail(request):
    """User profile view"""
    current_user = request.user
    context = {'username': current_user.username,
               'total_matches': int(current_user.profile.total_matches()),
               'matches_won': int(current_user.profile.matches_won),
               'win_rate': current_user.profile.success_rate()
    }
    return render(request, 'game/user_detail.html', context)

def leaderboard(request):
    """Leaderboard view"""
    ranking = Profile.objects.order_by('-matches_won')[:5]
    rates = []
    users = []
    for i in ranking:
        current_user = i.user
        users.append(current_user.username)
        rates.append(i.success_rate)
    ranking = zip(users, rates)
    context = {'text': 'Leaderboard goes here',
    'ranking': ranking}
    return render(request, 'game/leaderboard.html', context)

def play(request):
    """Game view"""
    #location = get_object_or_404(Location, location_text=location_name)
    #context = {'location': location}
    return render(request, 'game/play.html')

def location_json(request):
    """Retrieving location information"""
    location_list = Location.objects.all()
    location_json_list = serializers.serialize('json', location_list)
    # Convert JSON to python dict
    location_object = json.loads(location_json_list)

    # Create a new object and make the keys be the location names so it's easier to search later
    output_object = {}
    for location in location_object:
        location_text = location['fields']['location_text']
        output_object[location_text] = location
    return HttpResponse(json.dumps(output_object), content_type='application/javascript')

def safezone_json(request):
    """Retrieving safezone locations"""
    safezone_list = Safezone.objects.all()
    safezone_json_list = serializers.serialize('json', safezone_list)
    # Convert JSON to python dict
    safezone_object = json.loads(safezone_json_list)

    # Create a new object and make the keys be the location names so it's easier to search later
    output_object = {}
    for location in safezone_object:
        location_text = location['fields']['location_text']
        output_object[location_text] = location
    return HttpResponse(json.dumps(output_object), content_type='application/javascript')

def win(request, location_name):
    """Saving matches won to database"""
    location = get_object_or_404(Location, location_text=location_name)
    location.matches_won += 1
    location.save()

    if request.user.is_authenticated:
        current_user = request.user
        current_user.profile.matches_won += 1
        current_user.profile.save()

    return HttpResponse("matches_won increased by 1")

def lose(request, location_name):
    """Saving matches lost to database"""
    location = get_object_or_404(Location, location_text=location_name)
    location.matches_lost += 1
    location.save()

    if request.user.is_authenticated:
        current_user = request.user
        current_user.profile.matches_lost += 1
        current_user.profile.save()

    return HttpResponse("matches_lost increased by 1")

def announcement_json(request):
    announcement_list = Announcement.objects.all()
    announcementJSON = serializers.serialize('json', announcement_list)
    # Convert JSON to python dict
    announcementObject = json.loads(announcementJSON)
    outputObject = {}
    for announcement in announcementObject:
        announcement_text = announcement['fields']['announcement_text']
        outputObject[announcement_text] = announcement
    return HttpResponse(json.dumps(outputObject), content_type='application/javascript')

def profile_json(request):
    pass
<|endoftext|>


<commit_before># vim:set et sts=4 sw=4:
#
# ibus - The Input Bus
#
# Copyright (c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
# Boston, MA  02111-1307  USA

import os
import sys
import getopt
import ibus
import config
import gtk

class GconfApplication:
    def __init__ (self):
        self.__conn = ibus.Connection()
        self.__conn.call_on_disconnection(self.__disconnected_cb)

        self.__ibus = self.__conn.get_ibus ()
        self.__config = config.ConfigProxy (self.__conn, "/org/freedesktop/IBus/Config", self.__ibus)

        self.__ibus.RegisterConfig (self.__config, True)

    def run (self):
        gtk.main ()

    def __disconnected_cb (self, conn):
        print "disconnected"
        gtk.main_quit ()

def launch_gconf ():
    # gtk.settings_get_default ().props.gtk_theme_name = "/home/phuang/.themes/aud-Default/gtk-2.0/gtkrc"
    # gtk.rc_parse ("./themes/default/gtkrc")
    GconfApplication ().run ()

def print_help (out, v = 0):
    print >> out, "-h, --help             show this message."
    print >> out, "-d, --daemonize        daemonize ibus"
    sys.exit (v)

def main ():
    daemonize = False
    shortopt = "hd"
    longopt = ["help", "daemonize"]
    try:
        opts, args = getopt.getopt (sys.argv[1:], shortopt, longopt)
    except getopt.GetoptError, err:
        print_help (sys.stderr, 1)

    for o, a in opts:
        if o in ("-h", "--help"):
            print_help (sys.stdout)
        elif o in ("-d", "--daemonize"):
            daemonize = True
        else:
            print >> sys.stderr, "Unknown argument: %s" % o
            print_help (sys.stderr, 1)

    if daemonize:
        if os.fork ():
            sys.exit ()

    launch_gconf ()

if __name__ == "__main__":
    main ()
<commit_msg>Refine coding style.<commit_after># vim:set et sts=4 sw=4:
#
# ibus - The Input Bus
#
# Copyright(c) 2007-2008 Huang Peng <shawn.p.huang@gmail.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or(at your option) any later version.
#
# This library 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
# Boston, MA  02111-1307  USA

import os
import sys
import getopt
import ibus
import config
import gtk

class GconfApplication:
    def __init__(self):
        self.__conn = ibus.Connection()
        self.__conn.call_on_disconnection(self.__disconnected_cb)

        self.__ibus = self.__conn.get_ibus()
        self.__config = config.ConfigProxy(self.__conn, "/org/freedesktop/IBus/Config", self.__ibus)

        self.__ibus.RegisterConfig(self.__config, True)

    def run(self):
        gtk.main()

    def __disconnected_cb(self, conn):
        print "disconnected"
        gtk.main_quit()

def launch_gconf():
    # gtk.settings_get_default().props.gtk_theme_name = "/home/phuang/.themes/aud-Default/gtk-2.0/gtkrc"
    # gtk.rc_parse("./themes/default/gtkrc")
    GconfApplication().run()

def print_help(out, v = 0):
    print >> out, "-h, --help             show this message."
    print >> out, "-d, --daemonize        daemonize ibus"
    sys.exit(v)

def main():
    daemonize = False
    shortopt = "hd"
    longopt = ["help", "daemonize"]
    try:
        opts, args = getopt.getopt(sys.argv[1:], shortopt, longopt)
    except getopt.GetoptError, err:
        print_help(sys.stderr, 1)

    for o, a in opts:
        if o in ("-h", "--help"):
            print_help(sys.stdout)
        elif o in ("-d", "--daemonize"):
            daemonize = True
        else:
            print >> sys.stderr, "Unknown argument: %s" % o
            print_help(sys.stderr, 1)

    if daemonize:
        if os.fork():
            sys.exit()

    launch_gconf()

if __name__ == "__main__":
    main()
<|endoftext|>


<commit_before>import vx
from vx.keybindings import bind, alt, ctrl, keys, KeybindingTable
import vx.movement as move
import vx.utils as utils
import vx.window
import vx.test
import vx.prompt
import vx.undo as undo

from ..pointer import panes, organizer

from functools import partial

def load(window):
    return Hopscotch(window)

class Hopscotch(KeybindingTable):
    def __init__(self, for_window):
        super().__init__()

        self.for_window = for_window

        self.mark = (1,1)

        # simple movement

        self.bind(ctrl + keys.n, move.down)
        self.bind(ctrl + keys.p, move.up)
        self.bind(ctrl + keys.b, move.left)
        self.bind(ctrl + keys.f, move.right)

        self.bind(ctrl + keys.a, move.bol)
        self.bind(ctrl + keys.e, move.eol)

        self.bind(ctrl + keys.v, partial(utils.repeat, move.down))
        self.bind(alt + keys.v, partial(utils.repeat, move.up))

        self.bind(alt + keys.langle, move.beg)
        self.bind(alt + keys.rangle, move.end)

        self.bind(keys.backspace, self.for_window.backspace)
        self.bind(ctrl + keys.d, self.for_window.delete)
        self.bind(keys.enter, partial(self.for_window.add_string, '\n'))

        self.bind(ctrl + keys.l, vx.window.center)

        self.bind(ctrl + keys.z, vx.suspend)

        self.bind(ctrl + keys.x - keys['0'], lambda: organizer.remove_pane(panes.focused))
        self.bind(ctrl + keys.x - keys['2'], lambda: panes.focused.split())
        self.bind(ctrl + keys.x - keys.o, lambda: organizer.next_pane())

        # misc.

        self.bind(ctrl + keys.x - ctrl + keys.c, vx.quit)
        self.bind(alt + keys.x, lambda: panes.focused.open_prompt(vx.prompt.exec_prompt))
        self.bind(ctrl + keys.s, lambda: panes.focused.open_prompt(vx.prompt.regex_prompt))

        self.bind(ctrl + keys.x - ctrl + keys.f, lambda: panes.focused.open_prompt(vx.prompt.file_prompt))

        self.bind(ctrl + keys.t, vx.test.run_tests)

        # undo/redo

        self.bind(ctrl + keys.underscore, self.for_window.undo)
        self.bind(alt + keys.forwardslash, self.for_window.redo)

        # save

        self.bind(ctrl + keys.x - ctrl + keys.s, lambda: self.for_window.save())

        # mark

        self.bind(ctrl + keys.at, self.save_mark)

        # copy/paste

        self.bind(ctrl + keys.k, self.cut_to_eol)
        self.bind(ctrl + keys.w, self.cut)
        self.bind(alt + keys.w, self.uncut)

    def save_mark(self):
        self.mark = self.for_window.cursor

    def uncut(self):
        if self.for_window.copystack.empty(): return

        (la, ca) = self.for_window.cursor
        to_insert = self.for_window.copystack.peek()
        self.for_window.add_string(to_insert)
        (lb, cb) = self.for_window.cursor
        self.for_window.dirty = True
        self.for_window.undo_tree.add(undo.addition(to_insert, la, ca, (la, ca, lb, cb)))

    def cut(self):
        (la, ca) = self.for_window.cursor
        (lb, cb) = self.mark
        forward = True
        if (la == lb and ca > cb) or lb < la:
            lb, cb, la, ca = la, ca, lb, cb
            forward = False
        text_between = vx.get_str_linecol_to_linecol_window(self.for_window, la, ca, lb, cb)
        self.for_window.copystack.push(text_between)
        self.for_window.remove_text(la, ca, lb, cb)
        self.for_window.dirty = True
        self.for_window.undo_tree.add(undo.removal(text_between, la, ca, (la, ca, lb, cb), forward))
        self.for_window.cursor = (la, ca)

    def cut_to_eol(self):
        (la, ca) = self.for_window.cursor
        with self.for_window.cursor_wander():
            move.eol()
            (lb, cb) = self.for_window.cursor
        text_between = vx.get_str_linecol_to_linecol_window(self.for_window, la, ca, lb, cb)
        self.for_window.copystack.push(text_between)
        self.for_window.remove_text(la, ca, lb, cb)
        self.for_window.dirty = True
        self.for_window.undo_tree.add(undo.removal(text_between, la, ca, (la, ca, lb, cb), True))
        self.for_window.cursor = (la, ca)
<commit_msg>Add column saving/restoring to hopscotch bindings<commit_after>import vx
from vx.keybindings import bind, alt, ctrl, keys, KeybindingTable
import vx.movement as move
import vx.utils as utils
import vx.window
import vx.test
import vx.prompt
import vx.undo as undo

from ..pointer import panes, organizer

from functools import partial

def load(window):
    return Hopscotch(window)

class Hopscotch(KeybindingTable):
    def __init__(self, for_window):
        super().__init__()

        self.for_window = for_window

        self.mark = (1,1)

        # simple movement

        def with_column_restore(f):
            with self.for_window.column_restore():
                return f()
        def with_column_save(f):
            with self.for_window.column_save():
                return f()

        self.bind(ctrl + keys.n, partial(with_column_restore, move.down))
        self.bind(ctrl + keys.p, partial(with_column_restore, move.up))
        self.bind(ctrl + keys.b, partial(with_column_save, move.left))
        self.bind(ctrl + keys.f, partial(with_column_save, move.right))

        self.bind(ctrl + keys.a, partial(with_column_save, move.bol))
        self.bind(ctrl + keys.e, partial(with_column_save, move.eol))

        self.bind(ctrl + keys.v, partial(utils.repeat, move.down))
        self.bind(alt + keys.v, partial(utils.repeat, move.up))

        self.bind(alt + keys.langle, partial(with_column_save, move.beg))
        self.bind(alt + keys.rangle, partial(with_column_save, move.end))

        self.bind(keys.backspace, partial(with_column_save, self.for_window.backspace))
        self.bind(ctrl + keys.d, partial(with_column_save, self.for_window.delete))
        self.bind(keys.enter, partial(self.for_window.add_string, '\n'))

        self.bind(ctrl + keys.l, vx.window.center)

        self.bind(ctrl + keys.z, vx.suspend)

        self.bind(ctrl + keys.x - keys['0'], lambda: organizer.remove_pane(panes.focused))
        self.bind(ctrl + keys.x - keys['2'], lambda: panes.focused.split())
        self.bind(ctrl + keys.x - keys.o, lambda: organizer.next_pane())

        # misc.

        self.bind(ctrl + keys.x - ctrl + keys.c, vx.quit)
        self.bind(alt + keys.x, lambda: panes.focused.open_prompt(vx.prompt.exec_prompt))
        self.bind(ctrl + keys.s, lambda: panes.focused.open_prompt(vx.prompt.regex_prompt))

        self.bind(ctrl + keys.x - ctrl + keys.f, lambda: panes.focused.open_prompt(vx.prompt.file_prompt))

        self.bind(ctrl + keys.t, vx.test.run_tests)

        # undo/redo

        self.bind(ctrl + keys.underscore, self.for_window.undo)
        self.bind(alt + keys.forwardslash, self.for_window.redo)

        # save

        self.bind(ctrl + keys.x - ctrl + keys.s, lambda: self.for_window.save())

        # mark

        self.bind(ctrl + keys.at, self.save_mark)

        # copy/paste

        self.bind(ctrl + keys.k, self.cut_to_eol)
        self.bind(ctrl + keys.w, self.cut)
        self.bind(alt + keys.w, self.uncut)

    def save_mark(self):
        self.mark = self.for_window.cursor

    def uncut(self):
        if self.for_window.copystack.empty(): return

        (la, ca) = self.for_window.cursor
        to_insert = self.for_window.copystack.peek()
        self.for_window.add_string(to_insert)
        (lb, cb) = self.for_window.cursor
        self.for_window.dirty = True
        self.for_window.undo_tree.add(undo.addition(to_insert, la, ca, (la, ca, lb, cb)))

    def cut(self):
        (la, ca) = self.for_window.cursor
        (lb, cb) = self.mark
        forward = True
        if (la == lb and ca > cb) or lb < la:
            lb, cb, la, ca = la, ca, lb, cb
            forward = False
        text_between = vx.get_str_linecol_to_linecol_window(self.for_window, la, ca, lb, cb)
        self.for_window.copystack.push(text_between)
        self.for_window.remove_text(la, ca, lb, cb)
        self.for_window.dirty = True
        self.for_window.undo_tree.add(undo.removal(text_between, la, ca, (la, ca, lb, cb), forward))
        self.for_window.cursor = (la, ca)

    def cut_to_eol(self):
        (la, ca) = self.for_window.cursor
        with self.for_window.cursor_wander():
            move.eol()
            (lb, cb) = self.for_window.cursor
        text_between = vx.get_str_linecol_to_linecol_window(self.for_window, la, ca, lb, cb)
        self.for_window.copystack.push(text_between)
        self.for_window.remove_text(la, ca, lb, cb)
        self.for_window.dirty = True
        self.for_window.undo_tree.add(undo.removal(text_between, la, ca, (la, ca, lb, cb), True))
        self.for_window.cursor = (la, ca)
<|endoftext|>


<commit_before>#!/usr/bin/env python2.5
#
# Copyright 2012 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

""" This module contains the object used to represent invitations and
requests between a user and an organization
"""
from django.utils.translation import ugettext
from google.appengine.ext import db
from soc.models.organization import Organization
from soc.models.profile import Profile

RESPONSE_STATE_ACCEPTED = 'Accepted'
RESPONSE_STATE_REJECTED = 'Rejected'
RESPONSE_STATE_UNREPLIED = 'Unreplied'
RESPONSE_STATE_WITHDRAWN = 'Withdrawn'

RESPONSE_STATES = [
    RESPONSE_STATE_UNREPLIED, RESPONSE_STATE_ACCEPTED,
    RESPONSE_STATE_REJECTED,RESPONSE_STATE_WITHDRAWN
    ]

STATUS_STATES = {
    'withdrawn':'Withdrawn',
    'accepted':'Accepted',
    'rejected':'Rejected',
    'user_action_req':'User Action Required',
    'org_action_req' : 'Org Action Required',
    'withdrawn' : 'Withdrawn'
    }

class Connection(db.Model):
  """Connection model.
  This model is intended to be used to represent either an invitation or
  request between a User and an Organization. The type of role to be granted
  to the user is determined by the role field and promotion is handled
  depending on the states of user and org acceptance. The methods below
  are simply convenience to clean up a lot of the logic in the connection
  module for determining valid actions.

  Parent: soc.models.user.User (also parent of self.profile here)
  """

  #: The User's state with respect to a given role.
  user_state = db.StringProperty(default='Unreplied',
      choices=RESPONSE_STATES)

  #: The Org's state with respect to a given role.
  org_state = db.StringProperty(default='Unreplied',
      choices=RESPONSE_STATES)

  role = db.StringProperty(default='Mentor',
      choices=['Mentor', 'Org Admin'])

  #: The organization entity involved in the connection for which a user
  #: may gain heightened privileges.
  organization = db.ReferenceProperty(Organization,
      required=True,
      collection_name='connections')

  #: Property for the ShowConnection page to keep track of the time that the
  #: connection was initiated.
  created_on = db.DateTimeProperty(auto_now_add=True)

  @staticmethod
  def allFields():
    """Returns a list of all names of fields in this model.
    """
    return ['user_state', 'org_state', 'role','organization',
        'profile', 'created_on']

  def isUserUnreplied(self):
    return self.user_state == RESPONSE_STATE_UNREPLIED

  def isOrgUnreplied(self):
    return self.org_state == RESPONSE_STATE_UNREPLIED

  def isUserAccepted(self):
    return self.user_state == RESPONSE_STATE_ACCEPTED

  def isOrgAccepted(self):
    return self.org_state == RESPONSE_STATE_ACCEPTED

  def isUserRejected(self):
    return self.user_state == RESPONSE_STATE_REJECTED

  def isOrgRejected(self):
    return self.org_state == RESPONSE_STATE_REJECTED

  def isUserWithdrawn(self):
    return self.user_state == RESPONSE_STATE_WITHDRAWN

  def isOrgWithdrawn(self):
    return self.org_state == RESPONSE_STATE_WITHDRAWN

  def isWithdrawn(self):
    return self.user_state == RESPONSE_STATE_WITHDRAWN \
        or self.org_state == RESPONSE_STATE_WITHDRAWN

  def isStalemate(self):
    return (self.user_state == RESPONSE_STATE_ACCEPTED \
        and self.org_state == RESPONSE_STATE_REJECTED) \
        or (self.user_state == RESPONSE_STATE_REJECTED \
        and self.org_state == RESPONSE_STATE_ACCEPTED)

  def isAccepted(self):
    return self.user_state == RESPONSE_STATE_ACCEPTED and \
        self.org_state == RESPONSE_STATE_ACCEPTED

  def keyName(self):
    """Returns a string which uniquely represents the entity.
    """
    return '/'.join([self.parent_key().name(), str(self.key().id())])

  def status(self):
    """Returns a simple status string based on which of the user/org
    properties has been set.
    """
    if self.user_state == 'Accepted' and self.org_state == 'Accepted':
      return STATUS_STATES['accepted']
    elif self.user_state == 'Withdrawn' or self.org_state == 'Withdrawn':
      return STATUS_STATES['withdrawn']
    elif self.user_state == 'Rejected' or self.org_state == 'Rejected':
      return STATUS_STATES['rejected']
    elif self.user_state == 'Accepted':
      return STATUS_STATES['org_action_req']
    elif self.org_state == 'Accepted':
      return STATUS_STATES['user_action_req']
    else:
      return ''

class AnonymousConnection(db.Model):
  """This model is intended for use as a placeholder Connection for the
  scenario in which an org admin attempts to send an email invitation to
  a person who does not have both a User entity and program Profile. This
  model is deleted and 'replaced' by an actual Connection object should
  the user decide to register.

  Parent: soc.models.org.Organization
  """

  #: A string to designate the role that will be recreated for the actual
  #: connection object.
  role = db.StringProperty(choices=['Mentor', 'Org Admin'])

  #: Hash hexdigest() of this object's key to save time when validating
  #: when the user registers.
  hash_id = db.StringProperty()

  #: The email to which the anonymous connection was sent; this should be
  #: queried against to prevent duplicate anonymous connections.
  email = db.StringProperty()
<commit_msg>Improve consistency of string constants.<commit_after>#!/usr/bin/env python2.5
#
# Copyright 2012 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

""" This module contains the object used to represent invitations and
requests between a user and an organization
"""
from django.utils.translation import ugettext
from google.appengine.ext import db
from soc.models.organization import Organization
from soc.models.profile import Profile

ORG_ADMIN_STATE = 'Org Admin'
MENTOR_STATE = 'Mentor'

RESPONSE_STATE_ACCEPTED = 'Accepted'
RESPONSE_STATE_REJECTED = 'Rejected'
RESPONSE_STATE_UNREPLIED = 'Unreplied'
RESPONSE_STATE_WITHDRAWN = 'Withdrawn'

RESPONSE_STATES = [
    RESPONSE_STATE_UNREPLIED,
    RESPONSE_STATE_ACCEPTED,
    RESPONSE_STATE_REJECTED,
    RESPONSE_STATE_WITHDRAWN
    ]

# Strings used in the dashboard (via the stauts() method below) to display
# short, simple status messages for connections based on its state.
STATUS_STATES = {
    'withdrawn':'Withdrawn',
    'accepted':'Accepted',
    'rejected':'Rejected',
    'user_action_req':'User Action Required',
    'org_action_req' : 'Org Action Required',
    }

class Connection(db.Model):
  """Connection model.
  This model is intended to be used to represent either an invitation or
  request between a User and an Organization. The type of role to be granted
  to the user is determined by the role field and promotion is handled
  depending on the states of user and org acceptance. The methods below
  are simply convenience to clean up a lot of the logic in the connection
  module for determining valid actions.

  Parent: soc.models.user.User (also parent of self.profile here)
  """

  #: The User's state with respect to a given role.
  user_state = db.StringProperty(default=RESPONSE_STATE_UNREPLIED,
      choices=RESPONSE_STATES)

  #: The Org's state with respect to a given role.
  org_state = db.StringProperty(default=RESPONSE_STATE_UNREPLIED,
      choices=RESPONSE_STATES)

  role = db.StringProperty(default=MENTOR_STATE,
      choices=[MENTOR_STATE, ORG_ADMIN_STATE])

  #: The organization entity involved in the connection for which a user
  #: may gain heightened privileges.
  organization = db.ReferenceProperty(Organization,
      required=True,
      collection_name='connections')

  #: Property for the ShowConnection page to keep track of the time that the
  #: connection was initiated.
  created_on = db.DateTimeProperty(auto_now_add=True)

  @staticmethod
  def allFields():
    """Returns a list of all names of fields in this model.
    """
    return ['user_state', 'org_state', 'role','organization',
        'profile', 'created_on']

  def isUserUnreplied(self):
    return self.user_state == RESPONSE_STATE_UNREPLIED

  def isOrgUnreplied(self):
    return self.org_state == RESPONSE_STATE_UNREPLIED

  def isUserAccepted(self):
    return self.user_state == RESPONSE_STATE_ACCEPTED

  def isOrgAccepted(self):
    return self.org_state == RESPONSE_STATE_ACCEPTED

  def isUserRejected(self):
    return self.user_state == RESPONSE_STATE_REJECTED

  def isOrgRejected(self):
    return self.org_state == RESPONSE_STATE_REJECTED

  def isUserWithdrawn(self):
    return self.user_state == RESPONSE_STATE_WITHDRAWN

  def isOrgWithdrawn(self):
    return self.org_state == RESPONSE_STATE_WITHDRAWN

  def isWithdrawn(self):
    return self.user_state == RESPONSE_STATE_WITHDRAWN \
        or self.org_state == RESPONSE_STATE_WITHDRAWN

  def isStalemate(self):
    return (self.user_state == RESPONSE_STATE_ACCEPTED \
        and self.org_state == RESPONSE_STATE_REJECTED) \
        or (self.user_state == RESPONSE_STATE_REJECTED \
        and self.org_state == RESPONSE_STATE_ACCEPTED)

  def isAccepted(self):
    return self.user_state == RESPONSE_STATE_ACCEPTED and \
        self.org_state == RESPONSE_STATE_ACCEPTED

  def keyName(self):
    """Returns a string which uniquely represents the entity.
    """
    return '/'.join([self.parent_key().name(), str(self.key().id())])

  def status(self):
    """Returns a simple status string based on which of the user/org
    properties has been set.
    """
    if self.user_state == RESPONSE_STATE_ACCEPTED and  \
        self.org_state == RESPONSE_STATE_ACCEPTED:
      return STATUS_STATES['accepted']
    elif self.user_state == RESPONSE_STATE_WITHDRAWN or \
        self.org_state == RESPONSE_STATE_WITHDRAWN:
      return STATUS_STATES['withdrawn']
    elif self.user_state == RESPONSE_STATE_REJECTED or \
        self.org_state == RESPONSE_STATE_REJECTED:
      return STATUS_STATES['rejected']
    elif self.user_state == RESPONSE_STATE_ACCEPTED:
      return STATUS_STATES['org_action_req']
    elif self.org_state == RESPONSE_STATE_ACCEPTED:
      return STATUS_STATES['user_action_req']
    else:
      return ''

class AnonymousConnection(db.Model):
  """This model is intended for use as a placeholder Connection for the
  scenario in which an org admin attempts to send an email invitation to
  a person who does not have both a User entity and program Profile. This
  model is deleted and 'replaced' by an actual Connection object should
  the user decide to register.

  Parent: soc.models.org.Organization
  """

  #: A string to designate the role that will be recreated for the actual
  #: connection object.
  role = db.StringProperty(choices=[MENTOR_STATE, ORG_ADMIN_STATE])

  #: Hash hexdigest() of this object's key to save time when validating
  #: when the user registers.
  hash_id = db.StringProperty()

  #: The email to which the anonymous connection was sent; this should be
  #: queried against to prevent duplicate anonymous connections.
  email = db.StringProperty()
<|endoftext|>


<commit_before>#!/usr/bin/python2.5
#
# Copyright 2008 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Views for Site Settings.
"""

__authors__ = [
    '"Sverre Rabbelier" <sverre@rabbelier.nl>',
  ]

from django import forms

from soc.logic import dicts
from soc.views.helper import access
from soc.views.models import document as document_view
from soc.views.models import presence_with_tos

import soc.models.site
import soc.logic.models.site
import soc.logic.dicts

class View(presence_with_tos.View):
  """View methods for the Document model.
  """

  def __init__(self, params=None):
    """Defines the fields and methods required for the base View class
    to provide the user with list, public, create, edit and delete views.

    Params:
      params: a dict with params for this View
    """

    rights = access.Checker(params)
    rights['unspecified'] = ['checkIsDeveloper']
    rights['any_access'] = ['allow']
    rights['show'] = ['allow']

    new_params = {}
    new_params['logic'] = soc.logic.models.site.logic
    new_params['rights'] = rights

    new_params['name'] = "Site Settings"
    new_params['name_plural'] = new_params['name']
    new_params['name_short'] = "Site"

    new_params['sidebar_defaults'] = [('/%s/edit', 'Edit %(name)s', 'edit')]
    new_params['sidebar_heading'] = new_params['name_short']

    new_params['public_template'] = 'soc/presence/public.html'
    new_params['edit_template'] = 'soc/site/edit.html'
    new_params['home_template'] = 'soc/site/home.html'

    new_params['extra_dynaexclude'] = ['is_enabled']
    new_params['create_extra_dynafields'] = {
        'link_id': forms.CharField(widget=forms.HiddenInput, required=True),
        }
    new_params['edit_dynafields'] = []

    patterns = []

    page_name = "Home Page"
    patterns += [(r'^$', 'soc.views.models.%(module_name)s.main_public',
                  page_name)]

    page_name = "Edit Site"
    patterns += [(r'^%(url_name)s/(?P<access_type>edit)$',
                  'soc.views.models.%(module_name)s.main_edit',
                  page_name)]

    new_params['extra_django_patterns'] = patterns

    params = dicts.merge(params, new_params)

    super(View, self).__init__(params=params)

  def getSidebarMenus(self, id, user, params=None):
    """See base.View.getSidebarMenus.

    Returns a custom sidebar entry for the 'site' singleton.
    """

    entity = self._logic.getSingleton()

    submenus = []

    if entity:
      submenus = document_view.view.getMenusForScope(entity, self._params)

    new_params = {}
    new_params['sidebar_additional'] = submenus

    params = dicts.merge(params, new_params)
    return super(View, self).getSidebarMenus(id, user, params=params)

  def mainPublic(self, request, page_name=None, **kwargs):
    """Displays the main site settings page.

    Args:
      request: the standard Django HTTP request object
      page_name: the page name displayed in templates as page and header title
      kwargs: not used
    """

    keys = self._logic.getKeyFieldNames()

    # No entity in this case, since Site key values are hard-coded for the
    # Site singleton, so pass in None to match parent method footprint.
    values = self._logic.getKeyValues(None)
    key_values = dicts.zip(keys, values)

    return self.home(request, "home", page_name=page_name, **key_values)

  def mainEdit(self, request, page_name=None, **kwargs):
    """Displays the edit page for the main site settings page.

    Args:
      request: the standard Django HTTP request object
      page_name: the page name displayed in templates as page and header title
      kwargs: not used
    """

    keys = self._logic.getKeyFieldNames()

    # No entity in this case, since Site key values are hard-coded for the
    # Site singleton, so pass in None to match parent method footprint.
    values = self._logic.getKeyValues(None)
    key_values = dicts.zip(keys, values)

    return self.edit(request, "edit", page_name, seed=key_values, **key_values)

view = View()

create = view.create
edit = view.edit
delete = view.delete
list = view.list
public = view.public
export = view.export
main_public = view.mainPublic
main_edit = view.mainEdit
home = view.home
<commit_msg>Added a 'create new document' link to site<commit_after>#!/usr/bin/python2.5
#
# Copyright 2008 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Views for Site Settings.
"""

__authors__ = [
    '"Sverre Rabbelier" <sverre@rabbelier.nl>',
  ]

from django import forms

from soc.logic import accounts
from soc.logic import dicts
from soc.views.helper import access
from soc.views.helper import redirects
from soc.views.models import document as document_view
from soc.views.models import presence_with_tos

import soc.models.site
import soc.logic.models.site
import soc.logic.dicts

class View(presence_with_tos.View):
  """View methods for the Document model.
  """

  def __init__(self, params=None):
    """Defines the fields and methods required for the base View class
    to provide the user with list, public, create, edit and delete views.

    Params:
      params: a dict with params for this View
    """

    rights = access.Checker(params)
    rights['unspecified'] = ['checkIsDeveloper']
    rights['any_access'] = ['allow']
    rights['show'] = ['allow']

    new_params = {}
    new_params['logic'] = soc.logic.models.site.logic
    new_params['rights'] = rights

    new_params['name'] = "Site Settings"
    new_params['name_plural'] = new_params['name']
    new_params['name_short'] = "Site"

    new_params['sidebar_defaults'] = [('/%s/edit', 'Edit %(name)s', 'edit')]
    new_params['sidebar_heading'] = new_params['name_short']

    new_params['public_template'] = 'soc/presence/public.html'
    new_params['edit_template'] = 'soc/site/edit.html'
    new_params['home_template'] = 'soc/site/home.html'

    new_params['extra_dynaexclude'] = ['is_enabled']
    new_params['create_extra_dynafields'] = {
        'link_id': forms.CharField(widget=forms.HiddenInput, required=True),
        }
    new_params['edit_dynafields'] = []

    patterns = []

    page_name = "Home Page"
    patterns += [(r'^$', 'soc.views.models.%(module_name)s.main_public',
                  page_name)]

    page_name = "Edit Site"
    patterns += [(r'^%(url_name)s/(?P<access_type>edit)$',
                  'soc.views.models.%(module_name)s.main_edit',
                  page_name)]

    new_params['extra_django_patterns'] = patterns

    params = dicts.merge(params, new_params)

    super(View, self).__init__(params=params)

  def getSidebarMenus(self, id, user, params=None):
    """See base.View.getSidebarMenus.

    Returns a custom sidebar entry for the 'site' singleton.
    """

    entity = self._logic.getSingleton()

    submenus = []

    if entity:
      submenus += document_view.view.getMenusForScope(entity, self._params)

    if accounts.isDeveloper(id, user):
      submenus += [(redirects.getCreateDocumentRedirect(entity, 'site'),
          "Create new document", 'any_access')]

    new_params = {}
    new_params['sidebar_additional'] = submenus

    params = dicts.merge(params, new_params)
    return super(View, self).getSidebarMenus(id, user, params=params)

  def mainPublic(self, request, page_name=None, **kwargs):
    """Displays the main site settings page.

    Args:
      request: the standard Django HTTP request object
      page_name: the page name displayed in templates as page and header title
      kwargs: not used
    """

    keys = self._logic.getKeyFieldNames()

    # No entity in this case, since Site key values are hard-coded for the
    # Site singleton, so pass in None to match parent method footprint.
    values = self._logic.getKeyValues(None)
    key_values = dicts.zip(keys, values)

    return self.home(request, "home", page_name=page_name, **key_values)

  def mainEdit(self, request, page_name=None, **kwargs):
    """Displays the edit page for the main site settings page.

    Args:
      request: the standard Django HTTP request object
      page_name: the page name displayed in templates as page and header title
      kwargs: not used
    """

    keys = self._logic.getKeyFieldNames()

    # No entity in this case, since Site key values are hard-coded for the
    # Site singleton, so pass in None to match parent method footprint.
    values = self._logic.getKeyValues(None)
    key_values = dicts.zip(keys, values)

    return self.edit(request, "edit", page_name, seed=key_values, **key_values)

view = View()

create = view.create
edit = view.edit
delete = view.delete
list = view.list
public = view.public
export = view.export
main_public = view.mainPublic
main_edit = view.mainEdit
home = view.home
<|endoftext|>


<commit_before># -*- encoding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
#    $Id$
#
#    This program 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 3 of the License, or
#    (at your option) any later version.
#
#    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import netsvc
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_SERIALIZABLE
from psycopg2.pool import ThreadedConnectionPool
from psycopg2.psycopg1 import cursor as psycopg1cursor

import psycopg2.extensions

psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)

types_mapping = {
    'date': (1082,),
    'time': (1083,),
    'datetime': (1114,),
}

def unbuffer(symb, cr):
    if symb is None: return None
    return str(symb)

def undecimalize(symb, cr):
    if symb is None: return None
    return float(symb)

for name, typeoid in types_mapping.items():
    psycopg2.extensions.register_type(psycopg2.extensions.new_type(typeoid, name, lambda x, cr: x))
psycopg2.extensions.register_type(psycopg2.extensions.new_type((700, 701, 1700,), 'float', undecimalize))

import tools
import re

from mx import DateTime as mdt
re_from = re.compile('.* from "?([a-zA-Z_0-9]+)"? .*$');
re_into = re.compile('.* into "?([a-zA-Z_0-9]+)"? .*$');

def log(msg, lvl=netsvc.LOG_DEBUG):
    logger = netsvc.Logger()
    logger.notifyChannel('sql', lvl, msg)

class Cursor(object):
    IN_MAX = 1000
    sql_from_log = {}
    sql_into_log = {}
    sql_log = False
    count = 0

    def check(f):
        from tools.func import wraps

        @wraps(f)
        def wrapper(self, *args, **kwargs):
            if not hasattr(self, '_obj'):
                raise psycopg2.ProgrammingError('Unable to use the cursor after having closing it')
            return f(self, *args, **kwargs)
        return wrapper

    def __init__(self, pool):
        self._pool = pool
        self._cnx = pool.getconn()
        self._obj = self._cnx.cursor(cursor_factory=psycopg1cursor)
        self.autocommit(False)
        self.dbname = pool.dbname

        from inspect import stack
        self.__caller = tuple(stack()[2][1:3])

    def __del__(self):
        if hasattr(self, '_obj'):
            # Oops. 'self' has not been closed explicitly.
            # The cursor will be deleted by the garbage collector,
            # but the database connection is not put back into the connection
            # pool, preventing some operation on the database like dropping it.
            # This can also lead to a server overload.
            msg = "Cursor not closed explicitly\n"  \
                  "Cursor was created at %s:%s" % self.__caller

            log(msg, netsvc.LOG_WARNING)
            self.close()

    @check
    def execute(self, query, params=None):
        self.count+=1
        if '%d' in query or '%f' in query:
            log(query, netsvc.LOG_WARNING)
            log("SQL queries mustn't containt %d or %f anymore. Use only %s", netsvc.LOG_WARNING)
            if params:
                query = query.replace('%d', '%s').replace('%f', '%s')

        if self.sql_log:
            now = mdt.now()

        try:
            res = self._obj.execute(query, params)
	except psycopg2.ProgrammingError, pe:
	    logger= netsvc.Logger()
	    logger.notifyChannel('sql_db', netsvc.LOG_ERROR, "Programming error: %s, in query %s" % (pe, query))
	    raise

        if self.sql_log:
            log("query: %s" % self._obj.query)
            self.count+=1
            res_from = re_from.match(query.lower())
            if res_from:
                self.sql_from_log.setdefault(res_from.group(1), [0, 0])
                self.sql_from_log[res_from.group(1)][0] += 1
                self.sql_from_log[res_from.group(1)][1] += mdt.now() - now
            res_into = re_into.match(query.lower())
            if res_into:
                self.sql_into_log.setdefault(res_into.group(1), [0, 0])
                self.sql_into_log[res_into.group(1)][0] += 1
                self.sql_into_log[res_into.group(1)][1] += mdt.now() - now
        return res

    def print_log(self):
        def process(type):
            sqllogs = {'from':self.sql_from_log, 'into':self.sql_into_log}
            if not sqllogs[type]:
                return
            sqllogitems = sqllogs[type].items()
            sqllogitems.sort(key=lambda k: k[1][1])
            sum = 0
            log("SQL LOG %s:" % (type,))
            for r in sqllogitems:
                log("table: %s: %s/%s" %(r[0], str(r[1][1]), r[1][0]))
                sum+= r[1][1]
            log("SUM:%s/%d" % (sum, self.count))
            sqllogs[type].clear()
        process('from')
        process('into')
        self.count = 0
        self.sql_log = False

    @check
    def close(self):
        self.print_log()
        self._obj.close()

        # This force the cursor to be freed, and thus, available again. It is
        # important because otherwise we can overload the server very easily
        # because of a cursor shortage (because cursors are not garbage
        # collected as fast as they should). The problem is probably due in
        # part because browse records keep a reference to the cursor.
        del self._obj
        self._pool.putconn(self._cnx)

    @check
    def autocommit(self, on):
        self._cnx.set_isolation_level([ISOLATION_LEVEL_SERIALIZABLE, ISOLATION_LEVEL_AUTOCOMMIT][bool(on)])

    @check
    def commit(self):
        return self._cnx.commit()

    @check
    def rollback(self):
        return self._cnx.rollback()

    @check
    def __getattr__(self, name):
        return getattr(self._obj, name)

class ConnectionPool(object):
    def __init__(self, pool, dbname):
        self.dbname = dbname
        self._pool = pool

    def cursor(self):
        return Cursor(self)

    def __getattr__(self, name):
        return getattr(self._pool, name)

class PoolManager(object):
    _pools = {}
    _dsn = None
    maxconn =  int(tools.config['db_maxconn']) or 64

    @classmethod
    def dsn(cls, db_name):
        if cls._dsn is None:
            cls._dsn = ''
            for p in ('host', 'port', 'user', 'password'):
                cfg = tools.config['db_' + p]
                if cfg:
                    cls._dsn += '%s=%s ' % (p, cfg)
        return '%s dbname=%s' % (cls._dsn, db_name)

    @classmethod
    def get(cls, db_name):
        if db_name not in cls._pools:
            logger = netsvc.Logger()
            try:
                logger.notifyChannel('dbpool', netsvc.LOG_INFO, 'Connecting to %s' % (db_name,))
                cls._pools[db_name] = ConnectionPool(ThreadedConnectionPool(1, cls.maxconn, cls.dsn(db_name)), db_name)
            except Exception, e:
                logger.notifyChannel('dbpool', netsvc.LOG_ERROR, 'Unable to connect to %s: %s' %
                                     (db_name, str(e)))
                raise
        return cls._pools[db_name]

    @classmethod
    def close(cls, db_name):
        if db_name in cls._pools:
            logger = netsvc.Logger()
            logger.notifyChannel('dbpool', netsvc.LOG_INFO, 'Closing all connections to %s' % (db_name,))
            cls._pools[db_name].closeall()
            del cls._pools[db_name]

def db_connect(db_name, serialize=0):
    return PoolManager.get(db_name)

def close_db(db_name):
    PoolManager.close(db_name)
    tools.cache.clean_caches_for_db(db_name)

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

<commit_msg>Always print SQL counters.<commit_after># -*- encoding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
#    $Id$
#
#    This program 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 3 of the License, or
#    (at your option) any later version.
#
#    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import netsvc
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_SERIALIZABLE
from psycopg2.pool import ThreadedConnectionPool
from psycopg2.psycopg1 import cursor as psycopg1cursor

import psycopg2.extensions

psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)

types_mapping = {
    'date': (1082,),
    'time': (1083,),
    'datetime': (1114,),
}

def unbuffer(symb, cr):
    if symb is None: return None
    return str(symb)

def undecimalize(symb, cr):
    if symb is None: return None
    return float(symb)

for name, typeoid in types_mapping.items():
    psycopg2.extensions.register_type(psycopg2.extensions.new_type(typeoid, name, lambda x, cr: x))
psycopg2.extensions.register_type(psycopg2.extensions.new_type((700, 701, 1700,), 'float', undecimalize))

import tools
import re

from mx import DateTime as mdt
re_from = re.compile('.* from "?([a-zA-Z_0-9]+)"? .*$');
re_into = re.compile('.* into "?([a-zA-Z_0-9]+)"? .*$');

def log(msg, lvl=netsvc.LOG_DEBUG):
    logger = netsvc.Logger()
    logger.notifyChannel('sql', lvl, msg)

sql_counter = 0

class Cursor(object):
    IN_MAX = 1000
    sql_from_log = {}
    sql_into_log = {}
    sql_log = False
    count = 0

    def check(f):
        from tools.func import wraps

        @wraps(f)
        def wrapper(self, *args, **kwargs):
            if not hasattr(self, '_obj'):
                raise psycopg2.ProgrammingError('Unable to use the cursor after having closed it')
            return f(self, *args, **kwargs)
        return wrapper

    def __init__(self, pool):
        self._pool = pool
        self._cnx = pool.getconn()
        self._obj = self._cnx.cursor(cursor_factory=psycopg1cursor)
        self.autocommit(False)
        self.dbname = pool.dbname

        from inspect import stack
        self.__caller = tuple(stack()[2][1:3])

    def __del__(self):
        if hasattr(self, '_obj'):
            # Oops. 'self' has not been closed explicitly.
            # The cursor will be deleted by the garbage collector,
            # but the database connection is not put back into the connection
            # pool, preventing some operation on the database like dropping it.
            # This can also lead to a server overload.
            msg = "Cursor not closed explicitly\n"  \
                  "Cursor was created at %s:%s" % self.__caller

            log(msg, netsvc.LOG_WARNING)
            self.close()

    @check
    def execute(self, query, params=None):
        self.count+=1
        if '%d' in query or '%f' in query:
            log(query, netsvc.LOG_WARNING)
            log("SQL queries cannot contain %d or %f anymore. Use only %s", netsvc.LOG_WARNING)
            if params:
                query = query.replace('%d', '%s').replace('%f', '%s')

        if self.sql_log:
            now = mdt.now()

        try:
            res = self._obj.execute(query, params)
	except psycopg2.ProgrammingError, pe:
	    logger= netsvc.Logger()
	    logger.notifyChannel('sql_db', netsvc.LOG_ERROR, "Programming error: %s, in query %s" % (pe, query))
	    raise

        if self.sql_log:
            log("query: %s" % self._obj.query)
            self.count+=1
            res_from = re_from.match(query.lower())
            if res_from:
                self.sql_from_log.setdefault(res_from.group(1), [0, 0])
                self.sql_from_log[res_from.group(1)][0] += 1
                self.sql_from_log[res_from.group(1)][1] += mdt.now() - now
            res_into = re_into.match(query.lower())
            if res_into:
                self.sql_into_log.setdefault(res_into.group(1), [0, 0])
                self.sql_into_log[res_into.group(1)][0] += 1
                self.sql_into_log[res_into.group(1)][1] += mdt.now() - now
        return res

    def print_log(self):
	global sql_counter
	sql_counter += self.count
        def process(type):
            sqllogs = {'from':self.sql_from_log, 'into':self.sql_into_log}
            sum = 0
            if sqllogs[type]:
                sqllogitems = sqllogs[type].items()
                sqllogitems.sort(key=lambda k: k[1][1])
                log("SQL LOG %s:" % (type,))
                for r in sqllogitems:
                    log("table: %s: %s/%s" %(r[0], str(r[1][1]), r[1][0]))
                    sum+= r[1][1]
                sqllogs[type].clear()
            log("SUM:%s/%d [%d]" % (sum, self.count,sql_counter))
        process('from')
        process('into')
        self.count = 0
        self.sql_log = False

    @check
    def close(self):
        self.print_log()
        self._obj.close()

        # This force the cursor to be freed, and thus, available again. It is
        # important because otherwise we can overload the server very easily
        # because of a cursor shortage (because cursors are not garbage
        # collected as fast as they should). The problem is probably due in
        # part because browse records keep a reference to the cursor.
        del self._obj
        self._pool.putconn(self._cnx)

    @check
    def autocommit(self, on):
        self._cnx.set_isolation_level([ISOLATION_LEVEL_SERIALIZABLE, ISOLATION_LEVEL_AUTOCOMMIT][bool(on)])

    @check
    def commit(self):
        return self._cnx.commit()

    @check
    def rollback(self):
        return self._cnx.rollback()

    @check
    def __getattr__(self, name):
        return getattr(self._obj, name)

class ConnectionPool(object):
    def __init__(self, pool, dbname):
        self.dbname = dbname
        self._pool = pool

    def cursor(self):
        return Cursor(self)

    def __getattr__(self, name):
        return getattr(self._pool, name)

class PoolManager(object):
    _pools = {}
    _dsn = None
    maxconn =  int(tools.config['db_maxconn']) or 64

    @classmethod
    def dsn(cls, db_name):
        if cls._dsn is None:
            cls._dsn = ''
            for p in ('host', 'port', 'user', 'password'):
                cfg = tools.config['db_' + p]
                if cfg:
                    cls._dsn += '%s=%s ' % (p, cfg)
        return '%s dbname=%s' % (cls._dsn, db_name)

    @classmethod
    def get(cls, db_name):
        if db_name not in cls._pools:
            logger = netsvc.Logger()
            try:
                logger.notifyChannel('dbpool', netsvc.LOG_INFO, 'Connecting to %s' % (db_name,))
                cls._pools[db_name] = ConnectionPool(ThreadedConnectionPool(1, cls.maxconn, cls.dsn(db_name)), db_name)
            except Exception, e:
                logger.notifyChannel('dbpool', netsvc.LOG_ERROR, 'Unable to connect to %s: %s' %
                                     (db_name, str(e)))
                raise
        return cls._pools[db_name]

    @classmethod
    def close(cls, db_name):
        if db_name in cls._pools:
            logger = netsvc.Logger()
            logger.notifyChannel('dbpool', netsvc.LOG_INFO, 'Closing all connections to %s' % (db_name,))
            cls._pools[db_name].closeall()
            del cls._pools[db_name]

def db_connect(db_name, serialize=0):
    return PoolManager.get(db_name)

def close_db(db_name):
    PoolManager.close(db_name)
    tools.cache.clean_caches_for_db(db_name)

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

<|endoftext|>