def init_database(app):
def init_disqus(app):
def init_publisher(app):
def get_client(key): class NewThreadForm(ReferrerCheckForm):
    subject = TextField('Subject', [Required()]) class NewPostForm(ReferrerCheckForm):
    message = TextAreaField('Message', [Required(), validators.Length(min=2)]) class Thread:
    @classmethod
    def format(cls, thread):
        return {
            'id': thread['id'],
            'title': thread['title'],
            'createdAtISO': thread['createdAt'].isoformat(),
            'category': thread['category'],
            'link': thread['link'],
            'posts': thread['posts'],
        }

    @classmethod
    def save(cls, thread):
        dt = datestr_to_datetime(thread['createdAt'])
        thread['createdAt'] = dt
        result = cls.format(thread)
        score = dt.strftime('%s.%m')
        threads.add(result, score)
        threads.add_to_set(result['id'], thread['posts'], _key='posts')
        return result

    @classmethod
    def get(cls, thread_id):
        result = threads.get(thread_id)
        if result is None:
            thread = disqusapi.threads.details(thread=thread_id, forum=app.config['DISQUS_FORUM'])
            result = cls.save(thread)
        return result

    @classmethod
    def list_by_author(cls, author_id, offset=0, limit=100):
        assert author_id == session['auth']['user_id']
        result = threads.list(author_id=author_id, offset=offset, limit=limit)
        if result is None:
            result = []
            for thread in api_call(disqusapi.users.listActiveThreads, forum=app.config['DISQUS_FORUM'], method='GET'):
                result.append(Thread.save(thread))
                score = thread['createdAt'].strftime('%s.%m')
                threads.add_to_set(thread['id'], score, author_id=author_id)
            result.reverse()

        return result

    @classmethod
    def list(cls, offset=0, limit=100):
        result = threads.list(offset=offset, limit=limit)
        if result is None:
            result = []
            for thread in Paginator(disqusapi.threads.list, forum=app.config['DISQUS_FORUM'], category=Category.get('General')['id'], method='GET'):
                result.append(Thread.save(thread))
            result.reverse()

        return result

    @classmethod
    def search(cls, query, limit=100):
        return threads.search(query, 'title', limit=limit) class Post:
    @classmethod
    def format(cls, post):
        avatar = secure_avatar(post['author']['avatar']['cache'])

        return {
            'id': post['id'],
            'avatar': avatar,
            'name': post['author']['username'],
            'createdAtISO': post['createdAt'].isoformat(),
            'message': post['message'],
            'hash': avatar_hash(post['author'])
        }

    @classmethod
    def save(cls, post, incr_posts=True):
        dt = datestr_to_datetime(post['createdAt'])
        post['createdAt'] = dt
        result = cls.format(post)
        score = dt.strftime('%s.%m')

        posts.add(result, score, thread_id=post['thread'])

        if incr_posts:
            threads.incr_counter(post['thread'], 'posts', 1)
            threads.incr_in_set(post['thread'], 1, _key='posts')
            threads.add_to_set(post['thread'], score)

        user = User.save(post['author'])
        users.add_to_set(user['id'], score, thread_id=post['thread'])
        threads.add_to_set(post['thread'], score)
        threads.add_to_set(post['thread'], score, author_id=post['author'])

        return result

    @classmethod
    def list_by_thread(cls, thread_id, offset=0, limit=100):
        result = posts.list(thread_id=thread_id, offset=offset, limit=limit)
        if result is None:
            result = []
            paginator = Paginator(disqusapi.threads.listPosts, thread=thread_id)
            for post in paginator:
                if post['author']['isAnonymous']:
                    continue
                result.append(cls.save(post, incr_posts=False))

        return result class Session:
    @classmethod
    def list(cls, offset=0, limit=100):
        thread_ids = []
        for talk in sorted(schedule.itervalues(), key=lambda x: (x['start'], x['room'])):
            thread_ids.append(talk['disqus:thread']['id'])

        if not thread_ids:
            return []

        thread_ids = thread_ids[:limit]

        result = threads.get_many(thread_ids)
        missing_thread_ids = [t for t, v in result.iteritems() if not v]
        if missing_thread_ids:
            thread_list = Paginator(disqusapi.threads.list, thread=missing_thread_ids, forum=app.config['DISQUS_FORUM'])
            for thread in thread_list:
                result[thread['id']] = Thread.save(thread)

        for thread in result.itervalues():
            if not thread:
                continue
            thread['session'] = schedule.get(thread.get('link'))

        return [result[t] for t in thread_ids if result.get(t)]

    @classmethod
    def list_active(cls, offset=0, limit=100):
        start = datetime.utcnow() - timedelta(minutes=10)
        end = start + timedelta(minutes=20)
        thread_ids = []
        for talk in sorted(schedule.itervalues(), key=lambda x: (x['start'], x['room'])):
            if talk['start'] > start and talk['start'] < end:
                thread_ids.append(talk['disqus:thread']['id'])

        if not thread_ids:
            return []

        thread_ids = thread_ids[:limit]

        result = threads.get_many(thread_ids)
        missing_thread_ids = [t for t, v in result.iteritems() if not v]
        if missing_thread_ids:
            thread_list = disqusapi.threads.list(thread=missing_thread_ids, forum=app.config['DISQUS_FORUM'])
            for thread in thread_list:
                result[thread['id']] = Thread.save(thread)

        for thread in result.itervalues():
            if not thread:
                continue
            thread['session'] = schedule.get(thread.get('link'))

        return [result[t] for t in thread_ids if result.get(t)]

    @classmethod
    def list_upcoming(cls, offset=0, limit=100):
        start = datetime.utcnow() - timedelta(minutes=10)
        # end = start + timedelta(minutes=30)
        num = 0
        thread_ids = []
        for talk in sorted(schedule.itervalues(), key=lambda x: (x['start'], x['room'])):
            if talk['end'] > start:  # and talk['start'] < end:
                thread_ids.append(talk['disqus:thread']['id'])
            if num > limit:
                return

        if not thread_ids:
            return []

        thread_ids = thread_ids[:limit]

        result = threads.get_many(thread_ids)
        missing_thread_ids = [t for t, v in result.iteritems() if not v]
        if missing_thread_ids:
            thread_list = disqusapi.threads.list(thread=missing_thread_ids, forum=app.config['DISQUS_FORUM'])
            for thread in thread_list:
                result[thread['id']] = Thread.save(thread)

        for thread in result.itervalues():
            if not thread:
                continue
            thread['session'] = schedule.get(thread.get('link'))

        return [result[t] for t in thread_ids if result.get(t)] class User:
    @classmethod
    def format(cls, user):
        return {
            'id': user['id'],
            'avatar': secure_avatar(user['avatar']['cache']),
            'name': user['username'],
            'hash': avatar_hash(user)
        }

    @classmethod
    def save(cls, user):
        result = cls.format(user)
        users.add(result, datetime.utcnow().strftime('%s.%m'))
        return result

    @classmethod
    def list(cls, offset=0, limit=100):
        return users.list(offset=offset, limit=limit)

    @classmethod
    def list_by_thread(cls, thread_id, offset=0, limit=100):
        return users.list(thread_id=thread_id, offset=offset, limit=limit)

    @classmethod
    def get_by_id(cls, user_id):
        return users.get(user_id) def login_required(func):
    @functools.wraps(func)
    def wrapped(*args, **kwargs):
        if 'auth' not in session:
            session['postauth'] = {
                'url': request.url,
                'form': request.form
            }
            return redirect(url_for('oauth_authorize'))
        return func(*args, **kwargs)
    return wrapped def api_call(func, **kwargs):
    access_token = get_access_token()
    try:
        if access_token:
            result = func(access_token=access_token, **kwargs)
        else:
            result = func(**kwargs)
    except InvalidAccessToken:
        raise Logout
    return result def timesince(dt):
    """
    Returns string representing "time since" or "time until" e.g.
    3 days ago, 5 hours from now etc.
    """

    now = datetime.utcnow()
    if now > dt:
        diff = now - dt
        dt_is_past = True
    else:
        diff = dt - now
        dt_is_past = False

    periods = (
        (diff.days / 365, "year", "years"),
        (diff.days / 30, "month", "months"),
        (diff.days / 7, "week", "weeks"),
        (diff.days, "day", "days"),
        (diff.seconds / 3600, "hour", "hours"),
        (diff.seconds / 60, "minute", "minutes"),
        (diff.seconds, "second", "seconds"),
    )

    for period, singular, plural in periods:

        if period:
            if dt_is_past:
                return "%d %s ago" % (period, singular if period == 1 else plural)
            return "in %d %s" % (period, singular if period == 1 else plural)

    return 'just now' def format_datetime(starts, ends):
    now = datetime.utcnow()
    if now > starts:
        if now < ends:
            return 'Ends in %s' % timeuntil(ends)
        return 'Ended %s' % timesince(ends)

    when = timeuntil(starts)
    if when == 'in 1 day':
        when = 'tomorrow'
    when += ', at %s' % convert_pycon_dt(starts).strftime('%I:%M %p')
    return 'Starts %s' % when def better_jsonify(json_obj, status=200):
    response = make_response(json.dumps(json_obj), status)
    response.headers['content-type'] = 'application/json'
    return response def convert_pycon_dt(value):
    return value.replace(tzinfo=pytz.UTC).astimezone(pacific) def base62_decode(string, alphabet=ALPHABET):
    """Decode a Base X encoded string into the number

    Arguments:
    - `string`: The encoded string
    - `alphabet`: The alphabet to use for encoding
    """
    base = len(alphabet)
    strlen = len(string)
    num = 0

    idx = 0
    for char in string:
        power = (strlen - (idx + 1))
        num += alphabet.index(char) * (base ** power)
        idx += 1

    return num def base62_encode(num, alphabet=ALPHABET):
    """Encode a number in Base X

    `num`: The number to encode
    `alphabet`: The alphabet to use for encoding
    """
    num = int(num)
    if (num == 0):
        return alphabet[0]
    arr = []
    base = len(alphabet)
    while num:
        rem = num % base
        num = num // base
        arr.append(alphabet[rem])
    arr.reverse()
    return ''.join(arr) class Publisher(object):
class View(object):
    def __init__(self, client, buffer_size=10000):
    def publish(self, key, value):
    def __init__(self, redis, publisher, name, version=1):
    def add(self, data, score, _key=None, **kwargs):
    def add_to_set(self, id, score, _data=None, _key=None, **kwargs):
    def incr_in_set(self, id, score, _data=None, _key=None, **kwargs):
    def remove(self, data, _key=None, **kwargs):
    def remove_from_set(self, id, _key=None, **kwargs):
    def incr_counter(self, id, key, amount=1):
    def get(self, id):
    def get_many(self, id_list):
    def list(self, offset=0, limit=-1, desc=True, _key=None, **kwargs):
    def search(self, query, field, limit=100, desc=True, _key=None, **kwargs):
    def get_key(self, keybase=None, **kwargs):
    def get_channel_key(self, key):
    def get_obj_key(self, id):
import itertools
import logging
import simplejson
from datetime import datetime, timedelta
from flask import request, session, render_template, redirect, url_for, jsonify
from jinja2 import Markup
from channels.app import app, disqusapi, schedule
from channels.forms import NewThreadForm, NewPostForm
from channels.models import Thread, Post, Session, User
from channels.oauth import login_required, api_call
from channels.utils import timesince, format_datetime, better_jsonify, convert_pycon_dt, \
  base62_decode, base62_encode
from channels.views import posts, users, threads
    return render_template('threads/new.html', form=form)


@app.route('/threads/mine', methods=['GET'])
@login_required
def my_threads():
    thread_list = Thread.list_by_author(author_id=session['auth']['user_id'])

    return render_template('threads/mine.html', thread_list=thread_list)


@app.route('/threads/all', methods=['GET', 'POST'])
def thread_list():
    try:
        page = int(request.args.get('page', 1)) - 1
    except (TypeError, ValueError):
        return redirect(url_for('thread_list'))

    thread_list = Thread.list(offset=page * 50, limit=50 + (page * 1))
    has_next = len(thread_list) > 50
    has_prev = page > 1

    thread_list = thread_list[:50]

    return render_template('threads/all.html', **{
        'thread_list': thread_list,
        'next_page': (page + 2) if has_next else None,
        'prev_page': (page) if has_prev else None,
    })


@app.route('/threads/search', methods=['GET', 'POST'])
def thread_search():
    query = request.args.get('q')
    if query:
        thread_list = Thread.search(query=query, limit=50)
    else:
        thread_list = None

    return render_template('threads/search.html', **{
        'query': query,
        'thread_list': thread_list,
    })


@app.route('/sessions/all', methods=['GET', 'POST'])
def session_list():
    session_list = Session.list(limit=1000)

    sorter = lambda x: x['session']['start'].date()

    sessions_by_date = itertools.groupby(session_list, key=sorter)

    return render_template('sessions/all.html', **{
        'sessions_by_date': sessions_by_date,
    })


@app.route('/threads/<thread_id>', methods=['GET', 'POST'])
def thread_details(thread_id):
    if not thread_id.isdigit():
        try:
            thread_id = base62_decode(thread_id)
        except:
            return redirect('/')

    form = NewPostForm(get_form_from_session())

    thread = Thread.get(thread_id)

    channel_list = {
        'posts': posts.get_channel_key(posts.get_key(thread_id=thread_id)),
        'participants': users.get_channel_key(users.get_key(thread_id=thread_id)),
        'active_thread_list': threads.get_channel_key(threads.get_key()),
    }

    if thread['link'] in schedule:
        pycon_session = schedule[thread['link']]
    else:
        pycon_session = False

    if 'auth' in session:
        my_threads = Thread.list_by_author(author_id=session['auth']['user_id'])[:5]
        channel_list['my_thread_list'] = threads.get_channel_key(threads.get_key(author_id=thread_id))
    else:
        my_threads = []

    post_list = Post.list_by_thread(thread_id)[::-1]
    if 'auth' in session:
        current_user = User.get_by_id(session['auth']['user_id'])
    else:
        current_user = None

    return render_template('threads/details.html', **{
        'thread': thread,
        'form': form,
        'pycon_session': pycon_session,
        'participant_list': User.list_by_thread(thread_id) or [],
        'my_thread_list': my_threads,
        'active_thread_list': Thread.list(limit=10),
        'post_list': post_list,
        'channel_list': channel_list,
        'realtime_host': app.config.get('REALTIME_HOST'),
        'current_user': current_user,
        'do_troll': app.config.get('DING_ENABLED', False)
    })


@app.route('/posts/<thread_id>.json', methods=['GET'])
def get_posts(thread_id):
    return jsonify({
        'post_list': Post.list_by_thread(thread_id)[::-1]
    })


@app.route('/threads/<thread_id>/reply', methods=['GET', 'POST'])
@login_required
def new_post(thread_id):
    form = NewPostForm()
    if form.validate_on_submit():

        post = api_call(disqusapi.posts.create, thread=thread_id, message=form.message.data)