Voyage/voyage.py
2019-12-11 19:47:19 -05:00

120 lines
3.1 KiB
Python

#!/usr/bin/env python3
"""
A display interface for archived 4chan quest threads.
"""
from collections import defaultdict
from aiohttp import web
import jinja2
import aiohttp_jinja2
from aiohttp_jinja2 import render_template
import asyncpg
import uvloop
import config
import filters
uvloop.install()
routes = web.RouteTableDef()
@routes.get('/', name='index')
async def index(request):
"""The index page."""
async with request.app['pool'].acquire() as conn:
threads = await conn.fetch("SELECT * FROM thread ORDER BY time ASC")
return render_template("index.html", request, locals())
@routes.get(r'/{thread_id:\d+}', name='thread')
async def thread(request):
"""A single thread page."""
thread_id = int(request.match_info['thread_id'])
async with request.app['pool'].acquire() as conn:
posts = await conn.fetch(
"SELECT * FROM post WHERE thread_id = $1 ORDER BY id ASC",
thread_id)
tags_raw = await conn.fetch(
"SELECT tag.post_id, tag.name FROM tag "
"LEFT JOIN post ON (tag.post_id = post.id) "
"WHERE post.thread_id = $1",
thread_id)
links_raw = await conn.fetch(
"SELECT link.link_from, link.link_to FROM link "
"LEFT JOIN post ON (link.link_from = post.id) "
"WHERE post.thread_id = $1",
thread_id)
tags = defaultdict(list)
links = defaultdict(list)
backlinks = defaultdict(list)
for tag_raw in tags_raw:
tag = tags[tag_raw['post_id']]
tag.append(tag_raw['name'])
tags[tag_raw['post_id']] = tag
for link_raw in links_raw:
link = links[link_raw['link_from']]
link.append(link_raw['link_to'])
links[link_raw['link_from']] = link
backlink = backlinks[link_raw['link_to']]
backlink.append(link_raw['link_from'])
backlinks[link_raw['link_to']] = backlink
url_prefix = config.url_prefix
return render_template("thread.html", request, locals())
@routes.post('/add_tag', name='add_tag')
async def add_tag(request):
"""Adds a tag to a post."""
data = await request.json()
post_id = int(data.get('post_id'))
tag_name = data.get('tag_name')
async with request.app['pool'].acquire() as conn:
await conn.execute(
"INSERT INTO tag (post_id, name) VALUES ($1, $2)",
post_id, tag_name)
return web.json_response({'ok': True})
@routes.post('/remove_tag', name='remove_tag')
async def remove_tag(request):
"""Removes a tag to a post."""
data = await request.json()
post_id = int(data.get('post_id'))
tag_name = data.get('tag_name')
async with request.app['pool'].acquire() as conn:
await conn.execute(
"DELETE FROM tag WHERE post_id = $1 AND name = $2",
post_id, tag_name)
return web.json_response({'ok': True})
async def init_app():
"""Initializes the application."""
app = web.Application()
aiohttp_jinja2.setup(
app,
trim_blocks=True,
lstrip_blocks=True,
undefined=jinja2.StrictUndefined,
loader=jinja2.FileSystemLoader('templates'),
filters={
'quotelink': filters.quotelink,
},
)
app['pool'] = await asyncpg.create_pool(**config.db)
app.router.add_routes(routes)
app_wrap = web.Application()
app_wrap.add_subapp(config.url_prefix, app)
return app_wrap
if __name__ == "__main__":
app = init_app()
web.run_app(app, host='0.0.0.0', port=5450)