2019-12-06 18:13:43 -05:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
"""
|
|
|
|
A display interface for archived 4chan quest threads.
|
|
|
|
"""
|
2019-12-11 19:47:19 -05:00
|
|
|
from collections import defaultdict
|
|
|
|
|
2019-12-06 18:13:43 -05:00
|
|
|
from aiohttp import web
|
|
|
|
import jinja2
|
|
|
|
import aiohttp_jinja2
|
|
|
|
from aiohttp_jinja2 import render_template
|
|
|
|
import asyncpg
|
|
|
|
import uvloop
|
|
|
|
|
|
|
|
import config
|
2019-12-10 18:19:01 -05:00
|
|
|
import filters
|
2019-12-06 18:13:43 -05:00
|
|
|
|
|
|
|
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())
|
|
|
|
|
|
|
|
|
2019-12-10 18:19:01 -05:00
|
|
|
@routes.get(r'/{thread_id:\d+}', name='thread')
|
|
|
|
async def thread(request):
|
|
|
|
"""A single thread page."""
|
2019-12-11 19:47:19 -05:00
|
|
|
thread_id = int(request.match_info['thread_id'])
|
2019-12-10 18:19:01 -05:00
|
|
|
async with request.app['pool'].acquire() as conn:
|
2019-12-11 19:47:19 -05:00
|
|
|
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)
|
2022-08-17 21:19:14 -04:00
|
|
|
tags_vis = sorted(list(set(t[1] for t in tags_raw)))
|
2019-12-11 19:47:19 -05:00
|
|
|
|
|
|
|
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
|
2019-12-10 18:19:01 -05:00
|
|
|
|
|
|
|
return render_template("thread.html", request, locals())
|
|
|
|
|
|
|
|
|
2019-12-11 19:47:19 -05:00
|
|
|
@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})
|
|
|
|
|
|
|
|
|
2019-12-06 18:13:43 -05:00
|
|
|
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'),
|
2019-12-10 18:19:01 -05:00
|
|
|
filters={
|
|
|
|
'quotelink': filters.quotelink,
|
|
|
|
},
|
2019-12-06 18:13:43 -05:00
|
|
|
)
|
|
|
|
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)
|