Hotfix to fix broken badge search.
This commit is contained in:
parent
2f193be848
commit
337dd8af3e
2 changed files with 907 additions and 0 deletions
897
roles/badges-frontend/files/tahrir/views.py
Normal file
897
roles/badges-frontend/files/tahrir/views.py
Normal file
|
@ -0,0 +1,897 @@
|
||||||
|
import random
|
||||||
|
import transaction
|
||||||
|
import types
|
||||||
|
import sqlalchemy as sa
|
||||||
|
import velruse
|
||||||
|
import json as _json
|
||||||
|
import StringIO
|
||||||
|
import qrcode as qrcode_module
|
||||||
|
import docutils.examples
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from mako.template import Template as t
|
||||||
|
from pyramid.view import (
|
||||||
|
view_config,
|
||||||
|
forbidden_view_config,
|
||||||
|
)
|
||||||
|
|
||||||
|
from pyramid.response import Response
|
||||||
|
from pyramid.httpexceptions import (
|
||||||
|
HTTPFound,
|
||||||
|
HTTPGone,
|
||||||
|
HTTPNotFound,
|
||||||
|
HTTPForbidden,
|
||||||
|
)
|
||||||
|
|
||||||
|
from pyramid.security import (
|
||||||
|
authenticated_userid,
|
||||||
|
effective_principals,
|
||||||
|
remember,
|
||||||
|
forget,
|
||||||
|
)
|
||||||
|
from pyramid.settings import asbool
|
||||||
|
|
||||||
|
from tahrir_api.dbapi import TahrirDatabase
|
||||||
|
import tahrir_api.model as m
|
||||||
|
|
||||||
|
from tahrir.utils import strip_tags, generate_badge_yaml
|
||||||
|
import widgets
|
||||||
|
|
||||||
|
from moksha.wsgi.widgets.api import get_moksha_socket, LiveWidget
|
||||||
|
|
||||||
|
# Optional. Emit messages to the fedmsg bus.
|
||||||
|
fedmsg = None
|
||||||
|
try:
|
||||||
|
import fedmsg
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='admin', renderer='admin.mak', permission='admin')
|
||||||
|
def admin(request):
|
||||||
|
|
||||||
|
settings = request.registry.settings
|
||||||
|
|
||||||
|
# TODO: Check if I even need this anymore... leaving for now.
|
||||||
|
request.session['came_from'] = request.route_url('admin')
|
||||||
|
|
||||||
|
if authenticated_userid(request):
|
||||||
|
awarded_assertions = request.db.get_assertions_by_email(
|
||||||
|
authenticated_userid(request))
|
||||||
|
else:
|
||||||
|
awarded_assertions = None
|
||||||
|
|
||||||
|
# Handle any admin actions. These are done through POSTS via the
|
||||||
|
# HTML forms on the admin panel.
|
||||||
|
if request.POST:
|
||||||
|
if request.POST.get('add-person'):
|
||||||
|
# Email is a required field on the HTML form.
|
||||||
|
# Add a Badge to the DB.
|
||||||
|
request.db.add_person(request.POST.get('person-email'),
|
||||||
|
nickname=request.POST.get(
|
||||||
|
'person-nickname'),
|
||||||
|
website=request.POST.get(
|
||||||
|
'person-website'),
|
||||||
|
bio=request.POST.get(
|
||||||
|
'person-bio'))
|
||||||
|
elif request.POST.get('add-badge'):
|
||||||
|
# Add a Badge to the DB.
|
||||||
|
request.db.add_badge(request.POST.get('badge-name'),
|
||||||
|
request.POST.get('badge-image'),
|
||||||
|
request.POST.get('badge-description'),
|
||||||
|
request.POST.get('badge-criteria'),
|
||||||
|
request.POST.get('badge-issuer'),
|
||||||
|
request.POST.get('badge-tags'))
|
||||||
|
elif request.POST.get('add-invitation'):
|
||||||
|
# Add an Invitation to the DB.
|
||||||
|
try:
|
||||||
|
created_on = datetime.strptime(
|
||||||
|
request.POST.get('invitation-created'),
|
||||||
|
'%Y-%m-%d %H:%M')
|
||||||
|
except ValueError:
|
||||||
|
created_on = None # Will default to datetime.now()
|
||||||
|
|
||||||
|
try:
|
||||||
|
expires_on = datetime.strptime(
|
||||||
|
request.POST.get('invitation-expires'),
|
||||||
|
'%Y-%m-%d %H:%M')
|
||||||
|
except ValueError:
|
||||||
|
expires_on = None # Will default to datettime.now()
|
||||||
|
|
||||||
|
request.db.add_invitation(
|
||||||
|
request.POST.get('invitation-badge-id'),
|
||||||
|
created_on=created_on,
|
||||||
|
expires_on=expires_on,
|
||||||
|
created_by=request.POST.get('invitation-issuer-id'))
|
||||||
|
elif request.POST.get('add-issuer'):
|
||||||
|
# Add an Issuer to the DB.
|
||||||
|
request.db.add_issuer(
|
||||||
|
request.POST.get('issuer-origin'),
|
||||||
|
request.POST.get('issuer-name'),
|
||||||
|
request.POST.get('issuer-org'),
|
||||||
|
request.POST.get('issuer-contact'))
|
||||||
|
elif request.POST.get('add-assertion'):
|
||||||
|
# Add an Assertion to the DB.
|
||||||
|
try:
|
||||||
|
issued_on = datetime.strptime(
|
||||||
|
request.POST.get('assertion-issued-on'),
|
||||||
|
'%Y-%m-%d %H:%M')
|
||||||
|
except ValueError:
|
||||||
|
issued_on = None # Will default to datetime.now()
|
||||||
|
|
||||||
|
request.db.add_assertion(
|
||||||
|
request.POST.get('assertion-badge-id'),
|
||||||
|
request.POST.get('assertion-person-email'),
|
||||||
|
issued_on)
|
||||||
|
|
||||||
|
if fedmsg and settings.get('tahrir.use_fedmsg', False):
|
||||||
|
person = request.db.get_person(
|
||||||
|
person_email=request.POST.get('assertion-person-email'))
|
||||||
|
badge = request.db.get_badge(
|
||||||
|
badge_id=request.POST.get('assertion-badge-id'))
|
||||||
|
|
||||||
|
fedmsg.publish(
|
||||||
|
modname="fedbadges", topic="badge.award",
|
||||||
|
msg=dict(
|
||||||
|
badge=dict(
|
||||||
|
name=badge.name,
|
||||||
|
description=badge.description,
|
||||||
|
image_url=badge.image,
|
||||||
|
),
|
||||||
|
user=dict(
|
||||||
|
username=person.nickname,
|
||||||
|
badges_user_id=person.id
|
||||||
|
),
|
||||||
|
))
|
||||||
|
|
||||||
|
return dict(
|
||||||
|
auth_principals=effective_principals(request),
|
||||||
|
awarded_assertions=awarded_assertions,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='home', renderer='index.mak')
|
||||||
|
def index(request):
|
||||||
|
|
||||||
|
n = 5 # n is the number of items displayed in each column.
|
||||||
|
|
||||||
|
if authenticated_userid(request):
|
||||||
|
awarded_assertions = request.db.get_assertions_by_email(
|
||||||
|
authenticated_userid(request))
|
||||||
|
else:
|
||||||
|
awarded_assertions = None
|
||||||
|
# set came_from so we can get back home after openid auth.
|
||||||
|
request.session['came_from'] = request.route_url('home')
|
||||||
|
|
||||||
|
persons_assertions = request.db.get_all_assertions().join(
|
||||||
|
m.Person).filter(
|
||||||
|
m.Person.opt_out == False)
|
||||||
|
from collections import defaultdict
|
||||||
|
top_persons = defaultdict(int) # person: assertion count
|
||||||
|
for item in persons_assertions:
|
||||||
|
top_persons[item.person] += 1
|
||||||
|
|
||||||
|
top_persons_sorted = sorted(sorted(top_persons,
|
||||||
|
key=lambda person: person.id),
|
||||||
|
key=top_persons.get,
|
||||||
|
reverse=True)
|
||||||
|
# Limit the sorted top persons to the top 10% and then take
|
||||||
|
# a random sample of 5 persons from that pool.
|
||||||
|
num_users_at_top = max(int(len(top_persons_sorted) * 0.1),
|
||||||
|
min(len(top_persons_sorted), 5))
|
||||||
|
# This is not actually a sample yet, but it's about to be...
|
||||||
|
top_persons_sample = top_persons_sorted[:num_users_at_top]
|
||||||
|
try:
|
||||||
|
top_persons_sample = random.sample(top_persons_sample, 5)
|
||||||
|
except ValueError:
|
||||||
|
# The sample is probably larger than the num of top users,
|
||||||
|
# so let's just take all the users in the top 10%, in a
|
||||||
|
# random order.
|
||||||
|
random.shuffle(top_persons_sample)
|
||||||
|
|
||||||
|
# Get latest awards.
|
||||||
|
latest_awards = persons_assertions.order_by(
|
||||||
|
sa.desc(m.Assertion.issued_on)).limit(n).all()
|
||||||
|
|
||||||
|
# Register our websocket handler callback
|
||||||
|
if asbool(request.registry.settings['tahrir.use_websockets']):
|
||||||
|
socket = make_websocket_handler(request.registry.settings)
|
||||||
|
socket.display()
|
||||||
|
|
||||||
|
return dict(
|
||||||
|
auth_principals=effective_principals(request),
|
||||||
|
latest_awards=latest_awards,
|
||||||
|
newest_persons=request.db.get_all_persons().filter(
|
||||||
|
m.Person.opt_out == False).order_by(
|
||||||
|
sa.desc(m.Person.created_on)).limit(n).all(),
|
||||||
|
top_persons=top_persons,
|
||||||
|
top_persons_sample=top_persons_sample,
|
||||||
|
awarded_assertions=awarded_assertions,
|
||||||
|
moksha_socket=get_moksha_socket(request.registry.settings),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(context=types.FunctionType, permission='admin')
|
||||||
|
def action(context, request):
|
||||||
|
|
||||||
|
# Do the action
|
||||||
|
context()
|
||||||
|
|
||||||
|
return HTTPFound(location=request.route_url('home'))
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(context=m.Invitation, name='claim')
|
||||||
|
def invitation_claim(request):
|
||||||
|
""" Action that awards a person a badge after scanning a qrcode. """
|
||||||
|
|
||||||
|
settings = request.registry.settings
|
||||||
|
|
||||||
|
if request.context.expires_on < datetime.now():
|
||||||
|
return HTTPGone("That invitation is expired.")
|
||||||
|
|
||||||
|
if not authenticated_userid(request):
|
||||||
|
request.session['came_from'] = request.resource_url(
|
||||||
|
request.context, 'claim')
|
||||||
|
return HTTPFound(location=request.route_url('login'))
|
||||||
|
|
||||||
|
person = request.db.get_person(person_email=authenticated_userid(request))
|
||||||
|
|
||||||
|
# Check to see if the user already has the badge.
|
||||||
|
if request.context.badge in [a.badge for a in person.assertions]:
|
||||||
|
# TODO: Flash a message explaining that they already have the badge
|
||||||
|
return HTTPFound(location=request.route_url('home'))
|
||||||
|
|
||||||
|
result = request.db.add_assertion(request.context.badge_id,
|
||||||
|
person.email,
|
||||||
|
datetime.now())
|
||||||
|
|
||||||
|
if fedmsg and settings.get('tahrir.use_fedmsg', False):
|
||||||
|
badge = request.context.badge
|
||||||
|
fedmsg.publish(
|
||||||
|
modname="fedbadges", topic="badge.award",
|
||||||
|
msg=dict(
|
||||||
|
badge=dict(
|
||||||
|
name=badge.name,
|
||||||
|
description=badge.description,
|
||||||
|
image_url=badge.image,
|
||||||
|
),
|
||||||
|
user=dict(
|
||||||
|
username=person.nickname,
|
||||||
|
badges_user_id=person.id
|
||||||
|
),
|
||||||
|
))
|
||||||
|
|
||||||
|
# TODO -- return them to a page that auto-exports their badges.
|
||||||
|
# TODO -- flash and tell them they got the badge
|
||||||
|
return HTTPFound(location=request.route_url('home'))
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(context=m.Invitation, name='qrcode')
|
||||||
|
def invitation_qrcode(request):
|
||||||
|
""" Returns a raw dummy qrcode through to the user. """
|
||||||
|
|
||||||
|
if request.context.expires_on < datetime.now():
|
||||||
|
return HTTPGone("That invitation is expired.")
|
||||||
|
|
||||||
|
target = request.resource_url(request.context, 'claim')
|
||||||
|
img = qrcode_module.make(target)
|
||||||
|
stringstream = StringIO.StringIO()
|
||||||
|
img.save(stringstream)
|
||||||
|
return Response(
|
||||||
|
body=stringstream.getvalue(),
|
||||||
|
content_type='image/png',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='leaderboard', renderer='leaderboard.mak')
|
||||||
|
def leaderboard(request):
|
||||||
|
""" Render a top users view. """
|
||||||
|
if authenticated_userid(request):
|
||||||
|
awarded_assertions = request.db.get_assertions_by_email(
|
||||||
|
authenticated_userid(request))
|
||||||
|
else:
|
||||||
|
awarded_assertions = None
|
||||||
|
|
||||||
|
# Get top persons.
|
||||||
|
persons_assertions = request.db.get_all_assertions().join(m.Person)
|
||||||
|
from collections import defaultdict
|
||||||
|
top_persons = defaultdict(int) # person: assertion count
|
||||||
|
for item in persons_assertions:
|
||||||
|
top_persons[item.person] += 1
|
||||||
|
|
||||||
|
# top_persons and top_persons_sorted contain all persons, ordered
|
||||||
|
top_persons_sorted = sorted(sorted(top_persons,
|
||||||
|
key=lambda person: person.id),
|
||||||
|
key=top_persons.get,
|
||||||
|
reverse=True)
|
||||||
|
|
||||||
|
# Get total user count.
|
||||||
|
user_count = len(top_persons)
|
||||||
|
|
||||||
|
if authenticated_userid(request):
|
||||||
|
# Get rank.
|
||||||
|
try:
|
||||||
|
rank = top_persons_sorted.index(request.db.get_person(
|
||||||
|
person_email=authenticated_userid(
|
||||||
|
request))) + 1
|
||||||
|
except ValueError:
|
||||||
|
rank = 0
|
||||||
|
# Get percentile.
|
||||||
|
try:
|
||||||
|
percentile = (float(rank) / float(user_count)) * 100
|
||||||
|
except ZeroDivisionError:
|
||||||
|
percentile = 0
|
||||||
|
|
||||||
|
# Get a list of nearby competetors (5 users above the current
|
||||||
|
# user and 5 users ranked below).
|
||||||
|
competitors = top_persons_sorted[max(rank - 3, 0):\
|
||||||
|
min(rank + 2, len(top_persons_sorted))]
|
||||||
|
|
||||||
|
else:
|
||||||
|
rank = None
|
||||||
|
percentile = None
|
||||||
|
competitors = None
|
||||||
|
|
||||||
|
return dict(
|
||||||
|
auth_principals=effective_principals(request),
|
||||||
|
awarded_assertions=awarded_assertions,
|
||||||
|
top_persons=top_persons,
|
||||||
|
top_persons_sorted=top_persons_sorted,
|
||||||
|
rank=rank,
|
||||||
|
user_count=user_count,
|
||||||
|
percentile=percentile,
|
||||||
|
competitors=competitors,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='explore', renderer='explore.mak')
|
||||||
|
def explore(request):
|
||||||
|
|
||||||
|
# Check if a search has been done, and if so, show
|
||||||
|
# search results.
|
||||||
|
search_results = dict() # name: link
|
||||||
|
if request.POST:
|
||||||
|
if request.POST.get('badge-search'):
|
||||||
|
# badge-query is a required field on the template form.
|
||||||
|
badge_query = request.POST.get('badge-query')
|
||||||
|
matching_results = request.db.get_all_badges().filter(
|
||||||
|
sa.func.lower(m.Badge.name).like('%' + badge_query
|
||||||
|
+ '%') |
|
||||||
|
sa.func.lower(m.Badge.description).like('%' +
|
||||||
|
badge_query +
|
||||||
|
'%') |
|
||||||
|
sa.func.lower(m.Badge.tags).like('%' +
|
||||||
|
badge_query
|
||||||
|
+ '%')).all()
|
||||||
|
for r in matching_results:
|
||||||
|
search_results[r.name] = request.route_url('badge',
|
||||||
|
id=r.name.lower().replace(' ', '-'))
|
||||||
|
elif request.POST.get('person-search'):
|
||||||
|
# person-query is a required field on the template form.
|
||||||
|
person_query = request.POST.get('person-query')
|
||||||
|
matching_results = request.db.get_all_persons().filter(
|
||||||
|
((m.Person.nickname.like('%' + person_query
|
||||||
|
+ '%')) |
|
||||||
|
(m.Person.bio.like('%' + person_query
|
||||||
|
+ '%'))) &
|
||||||
|
(m.Person.opt_out == False)).all()
|
||||||
|
for r in matching_results:
|
||||||
|
search_results[r.nickname] = request.route_url(
|
||||||
|
'user', id=r.nickname)
|
||||||
|
elif request.POST.get('tag-search'):
|
||||||
|
# tag-query is a required field on the template form.
|
||||||
|
tag_query = request.POST.get('tag-query')
|
||||||
|
if request.POST.get('tag-match-all'):
|
||||||
|
return HTTPFound(location=request.route_url(
|
||||||
|
'tags', tags=tag_query, match='all'))
|
||||||
|
else:
|
||||||
|
return HTTPFound(location=request.route_url(
|
||||||
|
'tags', tags=tag_query, match='any'))
|
||||||
|
|
||||||
|
|
||||||
|
# Get awarded assertions.
|
||||||
|
if authenticated_userid(request):
|
||||||
|
awarded_assertions = request.db.get_assertions_by_email(
|
||||||
|
authenticated_userid(request))
|
||||||
|
else:
|
||||||
|
awarded_assertions = None
|
||||||
|
|
||||||
|
# Get some random badges (for discovery).
|
||||||
|
try:
|
||||||
|
random_badges = random.sample(request.db.get_all_badges().all(), 5)
|
||||||
|
except ValueError: # the sample is probably larger than the population
|
||||||
|
random_badges = request.db.get_all_badges().all()
|
||||||
|
|
||||||
|
# Get some random persons (for discovery).
|
||||||
|
try:
|
||||||
|
random_persons = random.sample(request.db.get_all_persons().filter(
|
||||||
|
m.Person.opt_out == False).all(), 5)
|
||||||
|
except ValueError: # the sample is probably larger than the population
|
||||||
|
random_persons = request.db.get_all_persons().filter(
|
||||||
|
m.Person.opt_out == False).all()
|
||||||
|
|
||||||
|
return dict(
|
||||||
|
auth_principals=effective_principals(request),
|
||||||
|
awarded_assertions=awarded_assertions,
|
||||||
|
random_badges=random_badges,
|
||||||
|
random_persons=random_persons,
|
||||||
|
search_results=search_results,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='badge', renderer='badge.mak')
|
||||||
|
def badge(request):
|
||||||
|
"""Render badge info page."""
|
||||||
|
# Get the badge to render info about.
|
||||||
|
badge_id = request.matchdict.get('id')
|
||||||
|
badge = request.db.get_badge(badge_id)
|
||||||
|
|
||||||
|
# if the badge isn't found, raise a 404
|
||||||
|
if not badge:
|
||||||
|
raise HTTPNotFound("No such badge %r" % badge_id)
|
||||||
|
|
||||||
|
# Get awarded assertions.
|
||||||
|
if authenticated_userid(request):
|
||||||
|
awarded_assertions = request.db.get_assertions_by_email(
|
||||||
|
authenticated_userid(request))
|
||||||
|
else:
|
||||||
|
awarded_assertions = None
|
||||||
|
|
||||||
|
# Get badge statistics.
|
||||||
|
# TODO: Perhaps abstract these statistics methods away somewhere?
|
||||||
|
try:
|
||||||
|
times_awarded = len(request.db.get_assertions_by_badge(badge_id))
|
||||||
|
last_awarded = request.db.get_all_assertions().filter(
|
||||||
|
sa.func.lower(m.Assertion.badge_id) == \
|
||||||
|
sa.func.lower(badge_id)).order_by(
|
||||||
|
sa.desc(m.Assertion.issued_on)).limit(1).one()
|
||||||
|
last_awarded_person = request.db.get_person(
|
||||||
|
id=last_awarded.person_id)
|
||||||
|
|
||||||
|
first_awarded = request.db.get_all_assertions().filter(
|
||||||
|
sa.func.lower(m.Assertion.badge_id) == \
|
||||||
|
sa.func.lower(badge_id)).order_by(
|
||||||
|
sa.asc(m.Assertion.issued_on)).limit(1).one()
|
||||||
|
first_awarded_person = request.db.get_person(
|
||||||
|
id=first_awarded.person_id)
|
||||||
|
|
||||||
|
percent_earned = float(times_awarded) / \
|
||||||
|
float(len(request.db.get_all_persons().all()))
|
||||||
|
except sa.orm.exc.NoResultFound: # This badge has never been awarded.
|
||||||
|
times_awarded = 0
|
||||||
|
last_awarded = None
|
||||||
|
last_awarded_person = None
|
||||||
|
first_awarded = None
|
||||||
|
first_awarded_person = None
|
||||||
|
percent_earned = 0
|
||||||
|
# Percent of people who have earned this badge
|
||||||
|
|
||||||
|
# Get badge description HTML from RST.
|
||||||
|
# Note: this html_body function wraps the output
|
||||||
|
# in a <divclass="document">.
|
||||||
|
badge_description_html = docutils.examples.html_body(badge.description)
|
||||||
|
|
||||||
|
return dict(
|
||||||
|
badge=badge,
|
||||||
|
badge_description_html=badge_description_html,
|
||||||
|
auth_principals=effective_principals(request),
|
||||||
|
awarded_assertions=awarded_assertions,
|
||||||
|
times_awarded=times_awarded,
|
||||||
|
last_awarded=last_awarded,
|
||||||
|
last_awarded_person=last_awarded_person,
|
||||||
|
first_awarded=first_awarded,
|
||||||
|
first_awarded_person=first_awarded_person,
|
||||||
|
percent_earned=percent_earned,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _badge_json_generator(request, badge_id, badge):
|
||||||
|
try:
|
||||||
|
times_awarded = len(request.db.get_assertions_by_badge(badge_id))
|
||||||
|
|
||||||
|
last_awarded = request.db.get_all_assertions().filter(
|
||||||
|
sa.func.lower(m.Assertion.badge_id) == \
|
||||||
|
sa.func.lower(badge_id)).order_by(
|
||||||
|
sa.desc(m.Assertion.issued_on)).limit(1).one()
|
||||||
|
|
||||||
|
last_awarded_person = request.db.get_person(
|
||||||
|
id=last_awarded.person_id)
|
||||||
|
|
||||||
|
first_awarded = request.db.get_all_assertions().filter(
|
||||||
|
sa.func.lower(m.Assertion.badge_id) == \
|
||||||
|
sa.func.lower(badge_id)).order_by(
|
||||||
|
sa.asc(m.Assertion.issued_on)).limit(1).one()
|
||||||
|
|
||||||
|
first_awarded_person = request.db.get_person(
|
||||||
|
id=first_awarded.person_id)
|
||||||
|
|
||||||
|
percent_earned = float(times_awarded) / \
|
||||||
|
float(len(request.db.get_all_persons().all()))
|
||||||
|
|
||||||
|
except sa.orm.exc.NoResultFound: # This badge has never been awarded.
|
||||||
|
times_awarded = 0
|
||||||
|
last_awarded = None
|
||||||
|
last_awarded_person = None
|
||||||
|
first_awarded = None
|
||||||
|
first_awarded_person = None
|
||||||
|
percent_earned = 0
|
||||||
|
|
||||||
|
if last_awarded:
|
||||||
|
last_awarded = float(last_awarded.issued_on.strftime('%s'))
|
||||||
|
|
||||||
|
if last_awarded_person:
|
||||||
|
last_awarded_person = last_awarded_person.nickname
|
||||||
|
|
||||||
|
if first_awarded:
|
||||||
|
first_awarded = float(first_awarded.issued_on.strftime('%s'))
|
||||||
|
|
||||||
|
if first_awarded_person:
|
||||||
|
first_awarded_person = first_awarded_person.nickname
|
||||||
|
|
||||||
|
if percent_earned:
|
||||||
|
percent_earned *= 100
|
||||||
|
|
||||||
|
return {
|
||||||
|
'id': badge.id,
|
||||||
|
'name': badge.name,
|
||||||
|
'description': badge.description,
|
||||||
|
'times_awarded': times_awarded,
|
||||||
|
'last_awarded': last_awarded,
|
||||||
|
'last_awarded_person': last_awarded_person,
|
||||||
|
'first_awarded': first_awarded,
|
||||||
|
'first_awarded_person': first_awarded_person,
|
||||||
|
'percent_earned': percent_earned,
|
||||||
|
'image': badge.image
|
||||||
|
}
|
||||||
|
|
||||||
|
@view_config(route_name='badge_json', renderer='json')
|
||||||
|
def badge_json(request):
|
||||||
|
"""Render badge JSON dump."""
|
||||||
|
# Get the badge to render info about.
|
||||||
|
badge_id = request.matchdict.get('id')
|
||||||
|
badge = request.db.get_badge(badge_id)
|
||||||
|
|
||||||
|
# if the badge isn't found, raise a 404
|
||||||
|
if not badge:
|
||||||
|
request.response.status = '404 Not Found'
|
||||||
|
return {"error": "No such badge exists."}
|
||||||
|
|
||||||
|
return _badge_json_generator(request, badge_id, badge)
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='user', renderer='user.mak')
|
||||||
|
def user(request):
|
||||||
|
"""Render user info page."""
|
||||||
|
|
||||||
|
# Grab a boolean out of the config
|
||||||
|
settings = request.registry.settings
|
||||||
|
allow_changenick = asbool(settings.get('tahrir.allow_changenick', True))
|
||||||
|
|
||||||
|
# Get awarded assertions.
|
||||||
|
if authenticated_userid(request):
|
||||||
|
awarded_assertions = request.db.get_assertions_by_email(
|
||||||
|
authenticated_userid(request))
|
||||||
|
else:
|
||||||
|
awarded_assertions = None
|
||||||
|
|
||||||
|
# So, here they can use their 'id' or their 'nickname'.
|
||||||
|
# We'll try nickname first since we want to encourage that (or whatever)
|
||||||
|
# and fall back to id if that fails. If both fail, raise a 404.
|
||||||
|
user_id = request.matchdict.get('id')
|
||||||
|
|
||||||
|
user = request.db.get_person(nickname=user_id)
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
try:
|
||||||
|
# We cast user_id to an integer so that Postgres doesn't
|
||||||
|
# get upset about comparing what is potentially a string
|
||||||
|
# to an integer column.
|
||||||
|
user = request.db.get_person(id=int(user_id))
|
||||||
|
except TypeError:
|
||||||
|
raise HTTPNotFound("No such user %r" % user_id)
|
||||||
|
|
||||||
|
# If we still haven't found anything, just give up.
|
||||||
|
if not user:
|
||||||
|
raise HTTPNotFound("No such user %r" % user_id)
|
||||||
|
|
||||||
|
if user.opt_out == True and user.email != authenticated_userid(request):
|
||||||
|
raise HTTPNotFound("User %r has opted out." % user_id)
|
||||||
|
|
||||||
|
if request.POST:
|
||||||
|
|
||||||
|
# Authz check
|
||||||
|
if authenticated_userid(request) != user.email:
|
||||||
|
raise HTTPForbidden("Unauthorized")
|
||||||
|
|
||||||
|
person = request.db.get_all_persons().filter_by(
|
||||||
|
email=authenticated_userid(request)).one()
|
||||||
|
|
||||||
|
if request.POST.get('change-nickname') and allow_changenick:
|
||||||
|
new_nick = request.POST.get('new-nickname')
|
||||||
|
person.nickname = new_nick
|
||||||
|
|
||||||
|
# The user's nickname has changed, so let's go to the new URL.
|
||||||
|
return HTTPFound(location=request.route_url('user', id=new_nick))
|
||||||
|
elif request.POST.get('deactivate-account'):
|
||||||
|
person.opt_out = True
|
||||||
|
elif request.POST.get('reactivate-account'):
|
||||||
|
person.opt_out = False
|
||||||
|
|
||||||
|
# Get user badges.
|
||||||
|
user_badges = [a.badge for a in user.assertions]
|
||||||
|
|
||||||
|
# Sort user badges by id.
|
||||||
|
user_badges = sorted(user_badges, key=lambda badge: badge.id)
|
||||||
|
|
||||||
|
# Get total number of unique badges in the system.
|
||||||
|
count_total_badges = len(request.db.get_all_badges().all())
|
||||||
|
|
||||||
|
# Get percentage of badges earned.
|
||||||
|
try:
|
||||||
|
percent_earned = (float(len(user_badges)) / \
|
||||||
|
float(count_total_badges)) * 100
|
||||||
|
except ZeroDivisionError:
|
||||||
|
percent_earned = 0
|
||||||
|
|
||||||
|
# Get invitations the user has created.
|
||||||
|
invitations = [i for i in request.db.get_invitations(user.id)\
|
||||||
|
if i.expires_on > datetime.now()]
|
||||||
|
|
||||||
|
return dict(
|
||||||
|
user=user,
|
||||||
|
user_badges=user_badges,
|
||||||
|
invitations=invitations,
|
||||||
|
percent_earned=percent_earned,
|
||||||
|
auth_principals=effective_principals(request),
|
||||||
|
awarded_assertions=awarded_assertions,
|
||||||
|
allow_changenick=allow_changenick,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='user_json', renderer='json')
|
||||||
|
def user_json(request):
|
||||||
|
"""Render user info JSON dump."""
|
||||||
|
|
||||||
|
# So, here they can use their 'id' or their 'nickname'.
|
||||||
|
# We'll try nickname first since we want to encourage that (or whatever)
|
||||||
|
# and fall back to id if that fails. If both fail, raise a 404.
|
||||||
|
user_id = request.matchdict.get('id')
|
||||||
|
|
||||||
|
user = request.db.get_person(nickname=user_id)
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
try:
|
||||||
|
# We cast user_id to an integer so that Postgres doesn't
|
||||||
|
# get upset about comparing what is potentially a string
|
||||||
|
# to an integer column.
|
||||||
|
user = request.db.get_person(id=int(user_id))
|
||||||
|
except TypeError:
|
||||||
|
request.response.status = '404 Not Found'
|
||||||
|
return {"error": "No such user exists."}
|
||||||
|
|
||||||
|
# If we still haven't found anything, just give up.
|
||||||
|
if not user:
|
||||||
|
request.response.status = '404 Not Found'
|
||||||
|
return {"error": "No such user exists."}
|
||||||
|
|
||||||
|
if user.opt_out == True and user.email != authenticated_userid(request):
|
||||||
|
request.response.status = '404 Not Found'
|
||||||
|
return {"error": "User has opted out."}
|
||||||
|
|
||||||
|
awarded_assertions = request.db.get_assertions_by_email(user.email)
|
||||||
|
|
||||||
|
# Get user badges.
|
||||||
|
user_badges = [a.badge for a in user.assertions]
|
||||||
|
|
||||||
|
# Sort user badges by id.
|
||||||
|
user_badges = sorted(user_badges, key=lambda badge: badge.id)
|
||||||
|
|
||||||
|
# Get total number of unique badges in the system.
|
||||||
|
count_total_badges = len(request.db.get_all_badges().all())
|
||||||
|
|
||||||
|
# Get percentage of badges earned.
|
||||||
|
try:
|
||||||
|
percent_earned = (float(len(user_badges)) / \
|
||||||
|
float(count_total_badges)) * 100
|
||||||
|
except ZeroDivisionError:
|
||||||
|
percent_earned = 0
|
||||||
|
|
||||||
|
|
||||||
|
assertions = []
|
||||||
|
for assertion in awarded_assertions:
|
||||||
|
assertions.append(
|
||||||
|
dict(
|
||||||
|
{'issued': float(assertion.issued_on.strftime('%s'))}.items() + \
|
||||||
|
_badge_json_generator(request, assertion.badge.id, assertion.badge).items()))
|
||||||
|
|
||||||
|
return {
|
||||||
|
'user': user.nickname,
|
||||||
|
'avatar': user.avatar_url(int(request.GET.get('size', 100))),
|
||||||
|
'percent_earned': percent_earned,
|
||||||
|
'assertions': assertions
|
||||||
|
}
|
||||||
|
|
||||||
|
@view_config(route_name='builder', renderer='builder.mak')
|
||||||
|
def builder(request):
|
||||||
|
if authenticated_userid(request):
|
||||||
|
awarded_assertions = request.db.get_assertions_by_email(
|
||||||
|
authenticated_userid(request))
|
||||||
|
else:
|
||||||
|
awarded_assertions = None
|
||||||
|
|
||||||
|
# set came_from so we can get back home after openid auth.
|
||||||
|
request.session['came_from'] = request.route_url('builder')
|
||||||
|
|
||||||
|
# get default creator field
|
||||||
|
default_creator = None
|
||||||
|
user = request.db.get_person(person_email=authenticated_userid(request))
|
||||||
|
if user:
|
||||||
|
default_creator = user.nickname or user.email
|
||||||
|
|
||||||
|
badge_yaml = None
|
||||||
|
if request.POST:
|
||||||
|
badge_yaml = generate_badge_yaml(request.POST)
|
||||||
|
|
||||||
|
return dict(
|
||||||
|
auth_principals=effective_principals(request),
|
||||||
|
awarded_assertions=awarded_assertions,
|
||||||
|
default_creator=default_creator,
|
||||||
|
badge_yaml=badge_yaml,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='tags', renderer='tags.mak')
|
||||||
|
def tags(request):
|
||||||
|
"""Render tag page."""
|
||||||
|
|
||||||
|
# Get awarded assertions.
|
||||||
|
if authenticated_userid(request):
|
||||||
|
awarded_assertions = request.db.get_assertions_by_email(
|
||||||
|
authenticated_userid(request))
|
||||||
|
else:
|
||||||
|
awarded_assertions = None
|
||||||
|
|
||||||
|
# Get badges matching tag.
|
||||||
|
tags = [t.strip() for t in request.matchdict.get('tags').split(',')]
|
||||||
|
if request.matchdict.get('match') == 'all':
|
||||||
|
badges = request.db.get_badges_from_tags(tags, match_all=True)
|
||||||
|
else:
|
||||||
|
badges = request.db.get_badges_from_tags(tags, match_all=False)
|
||||||
|
|
||||||
|
return dict(
|
||||||
|
tags=tags,
|
||||||
|
badges=badges,
|
||||||
|
auth_principals=effective_principals(request),
|
||||||
|
awarded_assertions=awarded_assertions,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(context=unicode)
|
||||||
|
def html(context, request):
|
||||||
|
return Response(context)
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(context=m.Assertion, renderer='json')
|
||||||
|
def json(context, request):
|
||||||
|
return context.__json__()
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(context='pyramid.httpexceptions.HTTPNotFound', renderer='404.mak')
|
||||||
|
def _404(request):
|
||||||
|
request.response.status = 404
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(context='pyramid.httpexceptions.HTTPServerError',
|
||||||
|
renderer='500.mak')
|
||||||
|
def _500(request):
|
||||||
|
request.response.status = 500
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='login', renderer='login.mak')
|
||||||
|
@forbidden_view_config(renderer='login.mak')
|
||||||
|
def login(request):
|
||||||
|
settings = request.registry.settings
|
||||||
|
ident = "openid_identifier=" + settings.get('tahrir.openid_identifier')
|
||||||
|
url = velruse.login_url(request, 'openid') + "?" + ident
|
||||||
|
return HTTPFound(location=url)
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(context='velruse.AuthenticationComplete')
|
||||||
|
def login_complete_view(request):
|
||||||
|
context = request.context
|
||||||
|
settings = request.registry.settings
|
||||||
|
|
||||||
|
nickname = context.profile['preferredUsername']
|
||||||
|
|
||||||
|
if asbool(settings.get('tahrir.use_openid_email')) \
|
||||||
|
and context.profile['emails']:
|
||||||
|
email = context.profile['emails'][0]
|
||||||
|
else:
|
||||||
|
ident = settings.get('tahrir.openid_identifier')
|
||||||
|
domain = '.'.join(ident.split('.')[-2:])
|
||||||
|
if domain.endswith('/'):
|
||||||
|
domain = domain[:-1]
|
||||||
|
email = nickname + "@" + domain
|
||||||
|
|
||||||
|
# Keep adding underscores until we get a default nickname
|
||||||
|
# that isn't already used.
|
||||||
|
while request.db.get_person(nickname=nickname):
|
||||||
|
nickname += '_'
|
||||||
|
|
||||||
|
if not request.db.get_person(person_email=email):
|
||||||
|
request.db.add_person(email=email, nickname=nickname)
|
||||||
|
|
||||||
|
headers = remember(request, email)
|
||||||
|
response = HTTPFound(location=request.session.get('came_from', '/'))
|
||||||
|
response.headerlist.extend(headers)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(context='velruse.AuthenticationDenied', renderer='json')
|
||||||
|
def login_denied_view(request):
|
||||||
|
# HAAACK -- if login fails, just try again.
|
||||||
|
return HTTPFound(location=request.route_url('login'))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='logout')
|
||||||
|
def logout(request):
|
||||||
|
headers = forget(request)
|
||||||
|
return HTTPFound(location=request.resource_url(request.context),
|
||||||
|
headers=headers)
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='assertion_widget',
|
||||||
|
renderer='assertion_widget.mak')
|
||||||
|
def assertion_widget(request):
|
||||||
|
person_id = request.matchdict.get('person')
|
||||||
|
badge_id = request.matchdict.get('badge')
|
||||||
|
user = request.db.get_person(id=person_id)
|
||||||
|
if not user:
|
||||||
|
raise HTTPNotFound("No such person %r" % person_id)
|
||||||
|
|
||||||
|
def get_assertion():
|
||||||
|
for assertion in user.assertions:
|
||||||
|
if assertion.badge.id == badge_id:
|
||||||
|
return assertion
|
||||||
|
raise HTTPNotFound("User does not have that badge")
|
||||||
|
|
||||||
|
assertion = get_assertion()
|
||||||
|
return dict(assertion=assertion)
|
||||||
|
|
||||||
|
|
||||||
|
def make_websocket_handler(settings):
|
||||||
|
""" Add a js snippet that listens over websockets to fedmsg.
|
||||||
|
|
||||||
|
It animates the "latest awards" pane on the frontpage.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class WebsocketHandler(LiveWidget):
|
||||||
|
topic = settings.get("tahrir.websocket.topic")
|
||||||
|
onmessage = """
|
||||||
|
(function(json){
|
||||||
|
// TODO -- put the DOM manipulation stuff here.
|
||||||
|
var user = json.msg.user.badges_user_id;
|
||||||
|
var badge = json.msg.badge.badge_id;
|
||||||
|
$.ajax({
|
||||||
|
url: "%s/_w/assertion/" + user + "/" + badge,
|
||||||
|
dataType: "html",
|
||||||
|
success: function (html) {
|
||||||
|
$("#latest-awards").prepend(html);
|
||||||
|
$("#latest-awards > div:first-child").hide();
|
||||||
|
$("#latest-awards > div:first-child").slideDown("slow");
|
||||||
|
$("#latest-awards > div:last-child").slideUp('slow', complete=function() {
|
||||||
|
$("#latest-awards > div:last-child").remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})(json);
|
||||||
|
""" % settings['tahrir.base_url']
|
||||||
|
backend = "websocket"
|
||||||
|
|
||||||
|
# Don't actually produce anything when you call .display() on this widget.
|
||||||
|
inline_engine_name = "mako"
|
||||||
|
template = ""
|
||||||
|
|
||||||
|
return WebsocketHandler
|
|
@ -43,6 +43,16 @@
|
||||||
notify:
|
notify:
|
||||||
- restart apache
|
- restart apache
|
||||||
|
|
||||||
|
- name: hotfix - fix badges search 500 error with postgres
|
||||||
|
copy: >
|
||||||
|
src=tahrir/views.py
|
||||||
|
dest=/usr/lib/python2.6/site-packages/tahrir/views.py
|
||||||
|
owner=root group=root mode=0644
|
||||||
|
tags:
|
||||||
|
- hotfix
|
||||||
|
notify:
|
||||||
|
- restart apache
|
||||||
|
|
||||||
- name: hotfix - allow velruse to do stateless openid
|
- name: hotfix - allow velruse to do stateless openid
|
||||||
copy: >
|
copy: >
|
||||||
src=openid.py
|
src=openid.py
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue