Implement pool merging

This commit is contained in:
Ruin0x11 2020-05-04 15:15:30 -07:00
parent ffba010ae4
commit 6b8e3f251f
8 changed files with 137 additions and 169 deletions

View file

@ -15,7 +15,7 @@
background: $top-navigation-color background: $top-navigation-color
.names .names
width: 84% width: 84%
.usages .post-count
text-align: center text-align: center
width: 8% width: 8%
.creation-time .creation-time

View file

@ -1,5 +1,5 @@
<div class='content-wrapper' id='pool'> <div class='content-wrapper' id='pool'>
<h1><%- ctx.pool.first_name %></h1> <h1><%- ctx.pool.names[0] %></h1>
<nav class='buttons'><!-- <nav class='buttons'><!--
--><ul><!-- --><ul><!--
--><li data-name='summary'><a href='<%- ctx.formatClientLink('pool', ctx.pool.id) %>'>Summary</a></li><!-- --><li data-name='summary'><a href='<%- ctx.formatClientLink('pool', ctx.pool.id) %>'>Summary</a></li><!--

View file

@ -11,9 +11,9 @@
</th> </th>
<th class='post-count'> <th class='post-count'>
<% if (ctx.parameters.query == 'sort:post-count') { %> <% if (ctx.parameters.query == 'sort:post-count') { %>
<a href='<%- ctx.formatClientLink('pools', {query: '-sort:post-count'}) %>'>Post Count</a> <a href='<%- ctx.formatClientLink('pools', {query: '-sort:post-count'}) %>'>Post count</a>
<% } else { %> <% } else { %>
<a href='<%- ctx.formatClientLink('pools', {query: 'sort:post-count'}) %>'>Post Count</a> <a href='<%- ctx.formatClientLink('pools', {query: 'sort:post-count'}) %>'>Post count</a>
<% } %> <% } %>
</th> </th>
<th class='creation-time'> <th class='creation-time'>

View file

@ -105,13 +105,13 @@ class PoolController {
this._view.clearMessages(); this._view.clearMessages();
this._view.disableForm(); this._view.disableForm();
e.detail.pool e.detail.pool
.merge(e.detail.targetPoolName, e.detail.addAlias) .merge(e.detail.targetPoolId, e.detail.addAlias)
.then(() => { .then(() => {
this._view.showSuccess('Pool merged.'); this._view.showSuccess('Pool merged.');
this._view.enableForm(); this._view.enableForm();
router.replace( router.replace(
uri.formatClientLink( uri.formatClientLink(
'pool', e.detail.targetPoolName, 'merge'), 'pool', e.detail.targetPoolId, 'merge'),
null, false); null, false);
}, error => { }, error => {
this._view.showError(error.message); this._view.showError(error.message);

View file

@ -64,10 +64,6 @@ class PoolInputControl extends events.EventTarget {
verticalShift: -2 verticalShift: -2
}); });
// dom events
this._poolInputNode.addEventListener(
'keydown', e => this._evtInputKeyDown(e));
// show // show
this._hostNode.style.display = 'none'; this._hostNode.style.display = 'none';
this._hostNode.parentNode.insertBefore( this._hostNode.parentNode.insertBefore(
@ -117,23 +113,6 @@ class PoolInputControl extends events.EventTarget {
this.dispatchEvent(new CustomEvent('change')); this.dispatchEvent(new CustomEvent('change'));
} }
_evtAddPoolButtonClick(e) {
// TODO
// e.preventDefault();
// this.addPoolByName(this._poolInputNode.value, SOURCE_USER_INPUT);
// this._poolInputNode.value = '';
}
_evtInputKeyDown(e) {
// TODO
if (e.which == KEY_RETURN || e.which == KEY_SPACE) {
e.preventDefault();
// this._hideAutoComplete();
// this.addPoolByText(this._poolInputNode.value, SOURCE_USER_INPUT);
// this._poolInputNode.value = '';
}
}
_createListItemNode(pool) { _createListItemNode(pool) {
const className = pool.category ? const className = pool.category ?
misc.makeCssName(pool.category, 'pool') : misc.makeCssName(pool.category, 'pool') :
@ -159,10 +138,6 @@ class PoolInputControl extends events.EventTarget {
'href', uri.formatClientLink( 'href', uri.formatClientLink(
'posts', {query: "pool:" + pool.id})); 'posts', {query: "pool:" + pool.id}));
searchLinkNode.textContent = pool.names[0] + ' '; searchLinkNode.textContent = pool.names[0] + ' ';
searchLinkNode.addEventListener('click', e => {
// TODO?
// e.preventDefault();
});
const usagesNode = document.createElement('span'); const usagesNode = document.createElement('span');
usagesNode.classList.add('pool-usages'); usagesNode.classList.add('pool-usages');

View file

@ -8,6 +8,7 @@ const TagList = require('./tag_list.js');
const NoteList = require('./note_list.js'); const NoteList = require('./note_list.js');
const CommentList = require('./comment_list.js'); const CommentList = require('./comment_list.js');
const PoolList = require('./pool_list.js'); const PoolList = require('./pool_list.js');
const Pool = require('./pool.js');
const misc = require('../util/misc.js'); const misc = require('../util/misc.js');
class Post extends events.EventTarget { class Post extends events.EventTarget {
@ -98,22 +99,36 @@ class Post extends events.EventTarget {
_savePoolPosts() { _savePoolPosts() {
const difference = (a, b) => a.filter(post => !b.hasPoolId(post.id)); const difference = (a, b) => a.filter(post => !b.hasPoolId(post.id));
// find the pools where the post was added or removed
const added = difference(this.pools, this._orig._pools); const added = difference(this.pools, this._orig._pools);
const removed = difference(this._orig._pools, this.pools); const removed = difference(this._orig._pools, this.pools);
let ops = []; let ops = [];
// update each pool's list of posts
for (let pool of added) { for (let pool of added) {
if (!pool.posts.hasPostId(this._id)) { let op = Pool.get(pool.id).then(response => {
pool.posts.addById(this._id); if (!response.posts.hasPostId(this._id)) {
ops.push(pool.save()); response.posts.addById(this._id);
return response.save();
} else {
return Promise.resolve(response);
} }
});
ops.push(op);
} }
for (let pool of removed) { for (let pool of removed) {
if (pool.posts.hasPostId(this._id)) { let op = Pool.get(pool.id).then(response => {
pool.posts.removeById(this._id); if (response.posts.hasPostId(this._id)) {
ops.push(pool.save()); response.posts.removeById(this._id);
return response.save();
} else {
return Promise.resolve(response);
} }
});
ops.push(op);
} }
return Promise.all(ops); return Promise.all(ops);
@ -160,7 +175,7 @@ class Post extends events.EventTarget {
api.post(uri.formatApiLink('posts'), detail, files); api.post(uri.formatApiLink('posts'), detail, files);
return apiPromise.then(response => { return apiPromise.then(response => {
if (this._pools !== this._orig._pools) { if (misc.arraysDiffer(this._pools, this._orig._pools)) {
return this._savePoolPosts() return this._savePoolPosts()
.then(() => Promise.resolve(response)); .then(() => Promise.resolve(response));
} }

View file

@ -14,6 +14,7 @@ class PoolMergeView extends events.EventTarget {
this._pool = ctx.pool; this._pool = ctx.pool;
this._hostNode = ctx.hostNode; this._hostNode = ctx.hostNode;
this._target_pool_id = null;
ctx.poolNamePattern = api.getPoolNameRegex(); ctx.poolNamePattern = api.getPoolNameRegex();
views.replaceContent(this._hostNode, template(ctx)); views.replaceContent(this._hostNode, template(ctx));
@ -22,9 +23,11 @@ class PoolMergeView extends events.EventTarget {
this._autoCompleteControl = new PoolAutoCompleteControl( this._autoCompleteControl = new PoolAutoCompleteControl(
this._targetPoolFieldNode, this._targetPoolFieldNode,
{ {
confirm: pool => confirm: pool => {
this._target_pool_id = pool.id;
this._autoCompleteControl.replaceSelectedText( this._autoCompleteControl.replaceSelectedText(
pool.names[0], false), pool.names[0], false);
}
}); });
} }
@ -56,7 +59,7 @@ class PoolMergeView extends events.EventTarget {
this.dispatchEvent(new CustomEvent('submit', { this.dispatchEvent(new CustomEvent('submit', {
detail: { detail: {
pool: this._pool, pool: this._pool,
targetPoolName: this._targetPoolFieldNode.value targetPoolId: this._target_pool_id
}, },
})); }));
} }

View file

@ -6,7 +6,6 @@ from szurubooru import config, db, model, errors, rest
from szurubooru.func import util, pool_categories, serialization, posts from szurubooru.func import util, pool_categories, serialization, posts
class PoolNotFoundError(errors.NotFoundError): class PoolNotFoundError(errors.NotFoundError):
pass pass
@ -35,6 +34,10 @@ class InvalidPoolDescriptionError(errors.ValidationError):
pass pass
class InvalidPoolRelationError(errors.ValidationError):
pass
def _verify_name_validity(name: str) -> None: def _verify_name_validity(name: str) -> None:
if util.value_exceeds_column_size(name, model.PoolName.name): if util.value_exceeds_column_size(name, model.PoolName.name):
raise InvalidPoolNameError('Name is too long.') raise InvalidPoolNameError('Name is too long.')
@ -211,45 +214,18 @@ def merge_pools(source_pool: model.Pool, target_pool: model.Pool) -> None:
raise InvalidPoolRelationError('Cannot merge pool with itself.') raise InvalidPoolRelationError('Cannot merge pool with itself.')
def merge_posts(source_pool_id: int, target_pool_id: int) -> None: def merge_posts(source_pool_id: int, target_pool_id: int) -> None:
pass alias1 = model.PoolPost
# alias1 = model.PostPool alias2 = sa.orm.util.aliased(model.PoolPost)
# alias2 = sa.orm.util.aliased(model.PostPool)
# update_stmt = (
# sa.sql.expression.update(alias1)
# .where(alias1.pool_id == source_pool_id))
# update_stmt = (
# update_stmt
# .where(
# ~sa.exists()
# .where(alias1.post_id == alias2.post_id)
# .where(alias2.pool_id == target_pool_id)))
# update_stmt = update_stmt.values(pool_id=target_pool_id)
# db.session.execute(update_stmt)
def merge_relations(
table: model.Base, source_pool_id: int, target_pool_id: int) -> None:
alias1 = table
alias2 = sa.orm.util.aliased(table)
update_stmt = ( update_stmt = (
sa.sql.expression.update(alias1) sa.sql.expression.update(alias1)
.where(alias1.parent_id == source_pool_id) .where(alias1.pool_id == source_pool_id))
.where(alias1.child_id != target_pool_id)
.where(
~sa.exists()
.where(alias2.child_id == alias1.child_id)
.where(alias2.parent_id == target_pool_id))
.values(parent_id=target_pool_id))
db.session.execute(update_stmt)
update_stmt = ( update_stmt = (
sa.sql.expression.update(alias1) update_stmt
.where(alias1.child_id == source_pool_id)
.where(alias1.parent_id != target_pool_id)
.where( .where(
~sa.exists() ~sa.exists()
.where(alias2.parent_id == alias1.parent_id) .where(alias1.post_id == alias2.post_id)
.where(alias2.child_id == target_pool_id)) .where(alias2.pool_id == target_pool_id)))
.values(child_id=target_pool_id)) update_stmt = update_stmt.values(pool_id=target_pool_id)
db.session.execute(update_stmt) db.session.execute(update_stmt)
merge_posts(source_pool.pool_id, target_pool.pool_id) merge_posts(source_pool.pool_id, target_pool.pool_id)
@ -316,7 +292,6 @@ def update_pool_description(pool: model.Pool, description: str) -> None:
pool.description = description or None pool.description = description or None
def update_pool_posts(pool: model.Pool, post_ids: List[int]) -> None: def update_pool_posts(pool: model.Pool, post_ids: List[int]) -> None:
assert pool assert pool
if _check_post_duplication(post_ids): if _check_post_duplication(post_ids):