Route for getting previous/next posts in pool

This commit is contained in:
Ruin0x11 2021-05-08 22:08:11 -07:00
parent 748f0e16eb
commit 8e8b15a1d8
9 changed files with 194 additions and 6 deletions

View file

@ -16,6 +16,14 @@ class PostMainController extends BasePostController {
constructor(ctx, editMode) { constructor(ctx, editMode) {
super(ctx); super(ctx);
let poolPostsAround = Promise.resolve({results: [], activePool: null})
if (api.hasPrivilege("pools.list") && api.hasPrivilege("pools.view")) {
poolPostsAround = PostList.getPoolPostsAround(
ctxt.parameters.id,
parameters ? parameters.query : null
);
}
let parameters = ctx.parameters; let parameters = ctx.parameters;
Promise.all([ Promise.all([
Post.get(ctx.parameters.id), Post.get(ctx.parameters.id),
@ -23,9 +31,10 @@ class PostMainController extends BasePostController {
ctx.parameters.id, ctx.parameters.id,
parameters ? parameters.query : null parameters ? parameters.query : null
), ),
poolPostsAround
]).then( ]).then(
(responses) => { (responses) => {
const [post, aroundResponse] = responses; const [post, aroundResponse, poolPostsAroundResponse] = responses;
// remove junk from query, but save it into history so that it can // remove junk from query, but save it into history so that it can
// be still accessed after history navigation / page refresh // be still accessed after history navigation / page refresh
@ -44,6 +53,8 @@ class PostMainController extends BasePostController {
this._post = post; this._post = post;
this._view = new PostMainView({ this._view = new PostMainView({
post: post, post: post,
poolPostsAround: poolPostsAroundResponse.results,
activePool: poolPostsAroundResponse.activePool,
editMode: editMode, editMode: editMode,
prevPostId: aroundResponse.prev prevPostId: aroundResponse.prev
? aroundResponse.prev.id ? aroundResponse.prev.id

View file

@ -7,7 +7,7 @@ const PoolNavigatorControl = require("../controls/pool_navigator_control.js");
const template = views.getTemplate("pool-navigator-list"); const template = views.getTemplate("pool-navigator-list");
class PoolNavigatorListControl extends events.EventTarget { class PoolNavigatorListControl extends events.EventTarget {
constructor(hostNode, a) { constructor(hostNode, pools) {
super(); super();
this._hostNode = hostNode; this._hostNode = hostNode;

View file

@ -16,6 +16,15 @@ class PostList extends AbstractList {
); );
} }
static getPoolPostsAround(id, searchQuery) {
return api.get(
uri.formatApiLink("post", id, "pool-posts-around", {
query: PostList._decorateSearchQuery(searchQuery || ""),
fields: "id",
})
);
}
static search(text, offset, limit, fields) { static search(text, offset, limit, fields) {
return api return api
.get( .get(

View file

@ -9,4 +9,5 @@ pillow>=4.3.0
pynacl>=1.2.1 pynacl>=1.2.1
pytz>=2018.3 pytz>=2018.3
pyRFC3339>=1.0 pyRFC3339>=1.0
alembic_utils>=0.5.6
youtube_dl youtube_dl

View file

@ -284,6 +284,19 @@ def get_posts_around(
) )
@rest.routes.get("/post/(?P<post_id>[^/]+)/pool-posts-around/?")
def get_pool_posts_around(
ctx: rest.Context, params: Dict[str, str]
) -> rest.Response:
auth.verify_privilege(ctx.user, "posts:list")
auth.verify_privilege(ctx.user, "pools:list")
auth.verify_privilege(ctx.user, "pools:view")
_search_executor_config.user = ctx.user
post = _get_post(params)
results = posts.get_pool_posts_around(post)
return posts.serialize_pool_posts_around(results)
@rest.routes.post("/posts/reverse-search/?") @rest.routes.post("/posts/reverse-search/?")
def get_posts_by_image( def get_posts_by_image(
ctx: rest.Context, _params: Dict[str, str] = {} ctx: rest.Context, _params: Dict[str, str] = {}

View file

@ -2,6 +2,7 @@ import hmac
import logging import logging
from datetime import datetime from datetime import datetime
from typing import Any, Callable, Dict, List, Optional, Tuple from typing import Any, Callable, Dict, List, Optional, Tuple
from collections import namedtuple
import sqlalchemy as sa import sqlalchemy as sa
@ -134,12 +135,16 @@ def get_post_content_path(post: model.Post) -> str:
) )
def get_post_thumbnail_path_from_id(post_id: int) -> str:
return "generated-thumbnails/%d_%s.jpg" % (
post_id,
get_post_security_hash(post_id),
)
def get_post_thumbnail_path(post: model.Post) -> str: def get_post_thumbnail_path(post: model.Post) -> str:
assert post assert post
return "generated-thumbnails/%d_%s.jpg" % ( return get_post_thumbnail_path_from_id(post.post_id)
post.post_id,
get_post_security_hash(post.post_id),
)
def get_post_thumbnail_backup_path(post: model.Post) -> str: def get_post_thumbnail_backup_path(post: model.Post) -> str:
@ -967,3 +972,69 @@ def search_by_image(image_content: bytes) -> List[Tuple[float, model.Post]]:
] ]
else: else:
return [] return []
PoolPostsAround = namedtuple('PoolPostsAround', 'pool prev_post next_post')
def get_pool_posts_around(post: model.Post) -> List[PoolPostsAround]:
around = dict()
pool_ids = set()
post_ids = set()
dbquery = """
SELECT around.ord, around.pool_id, around.post_id, around.delta
FROM pool_post pp,
LATERAL get_pool_posts_around(pp.pool_id, pp.post_id) around
WHERE pp.post_id = :post_id;
"""
for order, pool_id, post_id, delta in db.session.execute(dbquery, {"post_id": post.post_id}):
if pool_id not in around:
around[pool_id] = [None, None]
if delta < 0:
around[pool_id][0] = post_id
elif delta > 0:
around[pool_id][1] = post_id
pool_ids.add(pool_id)
post_ids.add(post_id)
pools = dict()
posts = dict()
for pool in db.session.query(model.Pool).filter(model.Pool.pool_id.in_(pool_ids)).all():
pools[pool.pool_id] = pool
for result in db.session.query(model.Post.post_id).filter(model.Post.post_id.in_(post_ids)).all():
post_id = result[0]
posts[post_id] = { "id": post_id, "thumbnailUrl": get_post_thumbnail_path_from_id(post_id) }
results = []
for pool_id, entry in around.items():
prev_post = None
next_post = None
if entry[0] is not None:
prev_post = posts[entry[0]]
if entry[1] is not None:
next_post = posts[entry[1]]
results.append(PoolPostsAround(pools[pool_id], prev_post, next_post))
return results
def sort_pool_posts_around(around: List[PoolPostsAround]) -> List[PoolPostsAround]:
return sorted(
around,
key=lambda entry: entry.pool.pool_id,
)
def serialize_pool_posts_around(around: List[PoolPostsAround]) -> Optional[rest.Response]:
return [
{
"pool": pools.serialize_micro_pool(entry.pool),
"prev_post": entry.prev_post,
"next_post": entry.next_post
}
for entry in sort_pool_posts_around(around)
]

View file

@ -11,6 +11,7 @@ import sys
from time import sleep from time import sleep
import alembic import alembic
from alembic_utils.replaceable_entity import register_entities
import sqlalchemy as sa import sqlalchemy as sa
@ -21,6 +22,9 @@ sys.path.append(os.path.join(dir_to_self, *[os.pardir] * 2))
import szurubooru.config # noqa: E402 import szurubooru.config # noqa: E402
import szurubooru.model.base # noqa: E402 import szurubooru.model.base # noqa: E402
from szurubooru.migrations.functions import get_pool_posts_around # noqa: E402
register_entities([get_pool_posts_around])
# fmt: on # fmt: on

View file

@ -0,0 +1,46 @@
from alembic_utils.pg_function import PGFunction
get_pool_posts_around = PGFunction.from_sql("""
CREATE OR REPLACE FUNCTION public.get_pool_posts_around(
P_POOL_ID int,
P_POST_ID int
)
RETURNS TABLE (
ORD int,
POOL_ID int,
POST_ID int,
DELTA int
)
LANGUAGE PLPGSQL
AS $$
BEGIN
RETURN QUERY WITH main AS (
SELECT * FROM pool_post WHERE pool_post.pool_id = P_POOL_ID AND pool_post.post_id = P_POST_ID
),
around AS (
(SELECT pool_post.ord,
pool_post.pool_id,
pool_post.post_id,
1 as delta,
main.ord AS target_ord,
main.pool_id AS target_pool_id
FROM pool_post, main
WHERE pool_post.ord > main.ord
AND pool_post.pool_id = main.pool_id
ORDER BY pool_post.ord ASC LIMIT 1)
UNION
(SELECT pool_post.ord,
pool_post.pool_id,
pool_post.post_id,
-1 as delta,
main.ord AS target_ord,
main.pool_id AS target_pool_id
FROM pool_post, main
WHERE pool_post.ord < main.ord
AND pool_post.pool_id = main.pool_id
ORDER BY pool_post.ord DESC LIMIT 1)
)
SELECT around.ord, around.pool_id, around.post_id, around.delta FROM around;
END
$$
""")

View file

@ -0,0 +1,33 @@
'''
add get pool posts around function
Revision ID: f0b8a4298dc7
Created at: 2021-05-08 21:23:48.782025
'''
import sqlalchemy as sa
from alembic import op
from alembic_utils.pg_function import PGFunction
from sqlalchemy import text as sql_text
revision = 'f0b8a4298dc7'
down_revision = 'adcd63ff76a2'
branch_labels = None
depends_on = None
def upgrade():
public_get_pool_posts_around = PGFunction(
schema="public",
signature="get_pool_posts_around( P_POOL_ID int, P_POST_ID int )",
definition='returns TABLE (\n ORD int,\n POOL_ID int,\n POST_ID int,\n DELTA int\n )\n LANGUAGE PLPGSQL\nAS $$\nBEGIN\n RETURN QUERY WITH main AS (\n SELECT * FROM pool_post WHERE pool_post.pool_id = P_POOL_ID AND pool_post.post_id = P_POST_ID\n ),\n around AS (\n (SELECT pool_post.ord,\n pool_post.pool_id,\n pool_post.post_id,\n 1 as delta,\n main.ord AS target_ord,\n main.pool_id AS target_pool_id\n FROM pool_post, main\n WHERE pool_post.ord > main.ord\n AND pool_post.pool_id = main.pool_id\n ORDER BY pool_post.ord ASC LIMIT 1)\n UNION\n (SELECT pool_post.ord,\n pool_post.pool_id,\n pool_post.post_id,\n -1 as delta,\n main.ord AS target_ord,\n main.pool_id AS target_pool_id\n FROM pool_post, main\n WHERE pool_post.ord < main.ord\n AND pool_post.pool_id = main.pool_id\n ORDER BY pool_post.ord DESC LIMIT 1)\n )\n SELECT around.ord, around.pool_id, around.post_id, around.delta FROM around;\nEND\n$$'
)
op.create_entity(public_get_pool_posts_around)
def downgrade():
public_get_pool_posts_around = PGFunction(
schema="public",
signature="get_pool_posts_around( P_POOL_ID int, P_POST_ID int )",
definition='returns TABLE (\n ORD int,\n POOL_ID int,\n POST_ID int,\n DELTA int\n )\n LANGUAGE PLPGSQL\nAS $$\nBEGIN\n RETURN QUERY WITH main AS (\n SELECT * FROM pool_post WHERE pool_post.pool_id = P_POOL_ID AND pool_post.post_id = P_POST_ID\n ),\n around AS (\n (SELECT pool_post.ord,\n pool_post.pool_id,\n pool_post.post_id,\n 1 as delta,\n main.ord AS target_ord,\n main.pool_id AS target_pool_id\n FROM pool_post, main\n WHERE pool_post.ord > main.ord\n AND pool_post.pool_id = main.pool_id\n ORDER BY pool_post.ord ASC LIMIT 1)\n UNION\n (SELECT pool_post.ord,\n pool_post.pool_id,\n pool_post.post_id,\n -1 as delta,\n main.ord AS target_ord,\n main.pool_id AS target_pool_id\n FROM pool_post, main\n WHERE pool_post.ord < main.ord\n AND pool_post.pool_id = main.pool_id\n ORDER BY pool_post.ord DESC LIMIT 1)\n )\n SELECT around.ord, around.pool_id, around.post_id, around.delta FROM around;\nEND\n$$'
)
op.drop_entity(public_get_pool_posts_around)