proposals-fix
All checks were successful
Deploy on push / deploy (push) Successful in 5s

This commit is contained in:
Untone 2024-11-02 13:35:30 +03:00
parent 1b43f742d3
commit 3f1ef8dfd8
3 changed files with 65 additions and 50 deletions

View File

@ -6,8 +6,6 @@ from sqlalchemy.sql.functions import coalesce
from cache.cache import cache_author, cache_topic from cache.cache import cache_author, cache_topic
from orm.author import Author from orm.author import Author
from orm.rating import is_negative, is_positive
from orm.reaction import Reaction, ReactionKind
from orm.shout import Shout, ShoutAuthor, ShoutTopic from orm.shout import Shout, ShoutAuthor, ShoutTopic
from orm.topic import Topic from orm.topic import Topic
from resolvers.follower import follow, unfollow from resolvers.follower import follow, unfollow
@ -17,7 +15,6 @@ from services.db import local_session
from services.notify import notify_shout from services.notify import notify_shout
from services.schema import mutation, query from services.schema import mutation, query
from services.search import search_service from services.search import search_service
from utils.diff import apply_diff, get_diff
from utils.logger import root_logger as logger from utils.logger import root_logger as logger
@ -329,40 +326,3 @@ async def delete_shout(_, info, shout_id: int):
return {"error": None} return {"error": None}
else: else:
return {"error": "access denied"} return {"error": "access denied"}
def handle_proposing(session, r, shout):
if is_positive(r.kind):
replied_reaction = session.query(Reaction).filter(Reaction.id == r.reply_to, Reaction.shout == r.shout).first()
if replied_reaction and replied_reaction.kind is ReactionKind.PROPOSE.value and replied_reaction.quote:
# patch all the proposals' quotes
proposals = (
session.query(Reaction)
.filter(
and_(
Reaction.shout == r.shout,
Reaction.kind == ReactionKind.PROPOSE.value,
)
)
.all()
)
for proposal in proposals:
if proposal.quote:
proposal_diff = get_diff(shout.body, proposal.quote)
proposal_dict = proposal.dict()
proposal_dict["quote"] = apply_diff(replied_reaction.quote, proposal_diff)
Reaction.update(proposal, proposal_dict)
session.add(proposal)
# patch shout's body
shout_dict = shout.dict()
shout_dict["body"] = replied_reaction.quote
Shout.update(shout, shout_dict)
session.add(shout)
session.commit()
if is_negative(r.kind):
# TODO: rejection logic
pass

48
resolvers/proposals.py Normal file
View File

@ -0,0 +1,48 @@
from sqlalchemy import and_
from orm.rating import is_negative, is_positive
from orm.reaction import Reaction, ReactionKind
from orm.shout import Shout
from services.db import local_session
from utils.diff import apply_diff, get_diff
def handle_proposing(kind: ReactionKind, reply_to: int, shout_id: int):
with local_session() as session:
if is_positive(kind):
replied_reaction = session.query(Reaction).filter(Reaction.id == reply_to, Reaction.shout == shout_id).first()
if replied_reaction and replied_reaction.kind is ReactionKind.PROPOSE.value and replied_reaction.quote:
# patch all the proposals' quotes
proposals = (
session.query(Reaction)
.filter(
and_(
Reaction.shout == shout_id,
Reaction.kind == ReactionKind.PROPOSE.value,
)
)
.all()
)
# patch shout's body
shout = session.query(Shout).filter(Shout.id == shout_id).first()
body = replied_reaction.quote
Shout.update(shout, { body })
session.add(shout)
session.commit()
# реакция содержит цитату -> обновляются все предложения
# (proposals) для соответствующего Shout.
for proposal in proposals:
if proposal.quote:
proposal_diff = get_diff(shout.body, proposal.quote)
proposal_dict = proposal.dict()
proposal_dict["quote"] = apply_diff(replied_reaction.quote, proposal_diff)
Reaction.update(proposal, proposal_dict)
session.add(proposal)
if is_negative(kind):
# TODO: rejection logic
pass

View File

@ -7,7 +7,7 @@ from orm.author import Author
from orm.rating import PROPOSAL_REACTIONS, RATING_REACTIONS, is_negative, is_positive from orm.rating import PROPOSAL_REACTIONS, RATING_REACTIONS, is_negative, is_positive
from orm.reaction import Reaction, ReactionKind from orm.reaction import Reaction, ReactionKind
from orm.shout import Shout from orm.shout import Shout
from resolvers.editor import handle_proposing from resolvers.proposals import handle_proposing
from resolvers.follower import follow from resolvers.follower import follow
from resolvers.stat import update_author_stat from resolvers.stat import update_author_stat
from services.auth import add_user_role, login_required from services.auth import add_user_role, login_required
@ -82,8 +82,12 @@ def get_reactions_with_stat(q, limit, offset):
with local_session() as session: with local_session() as session:
result_rows = session.execute(q) result_rows = session.execute(q)
for reaction, author, shout, commented_stat, rating_stat in result_rows: for reaction, author, shout, commented_stat, rating_stat in result_rows:
reaction.created_by = author if shout is None:
reaction.shout = shout logger.error(f"пустое поле Shout: {reaction.dict()}")
continue # Или обработайте иначе
reaction.created_by = author.dict()
reaction.shout = shout.dict()
reaction.stat = {"rating": rating_stat, "comments": commented_stat} reaction.stat = {"rating": rating_stat, "comments": commented_stat}
reactions.append(reaction) reactions.append(reaction)
@ -186,7 +190,7 @@ def set_unfeatured(session, shout_id):
session.commit() session.commit()
async def _create_reaction(session, info, shout, author_id: int, reaction) -> dict: async def _create_reaction(session, info, shout_dict, author_id: int, reaction) -> dict:
""" """
Create a new reaction and perform related actions such as updating counters and notification. Create a new reaction and perform related actions such as updating counters and notification.
@ -207,20 +211,21 @@ async def _create_reaction(session, info, shout, author_id: int, reaction) -> di
update_author_stat(author_id) update_author_stat(author_id)
# Handle proposal # Handle proposal
if r.reply_to and r.kind in PROPOSAL_REACTIONS and author_id in shout.authors: is_author = bool(list(filter(lambda x: x['id'] == int(author_id), [x for x in shout_dict['authors']])))
handle_proposing(session, r, shout) if r.reply_to and r.kind in PROPOSAL_REACTIONS and is_author:
handle_proposing(r.kind, r.reply_to, shout_dict['id'])
# Handle rating # Handle rating
if r.kind in RATING_REACTIONS: if r.kind in RATING_REACTIONS:
if check_to_unfeature(session, author_id, r): if check_to_unfeature(session, author_id, r):
set_unfeatured(session, shout.id) set_unfeatured(session, shout_dict['id'])
elif check_to_feature(session, author_id, r): elif check_to_feature(session, author_id, r):
await set_featured(session, shout.id) await set_featured(session, shout_dict['id'])
# Follow if liked # Follow if liked
if r.kind == ReactionKind.LIKE.value: if r.kind == ReactionKind.LIKE.value:
try: try:
follow(None, info, "shout", shout.slug) follow(None, info, "shout", shout_dict['slug'])
except Exception: except Exception:
pass pass
@ -292,6 +297,7 @@ async def create_reaction(_, info, reaction):
logger.debug(f"Loaded shout: {shout and shout.id}") logger.debug(f"Loaded shout: {shout and shout.id}")
if shout: if shout:
shout_dict = shout.dict()
reaction["created_by"] = author_id reaction["created_by"] = author_id
kind = reaction.get( kind = reaction.get(
"kind", ReactionKind.COMMENT.value if isinstance(reaction.get("body"), str) else None "kind", ReactionKind.COMMENT.value if isinstance(reaction.get("body"), str) else None
@ -305,6 +311,7 @@ async def create_reaction(_, info, reaction):
logger.error(f"Rating preparation error: {error_result}") logger.error(f"Rating preparation error: {error_result}")
return error_result return error_result
logger.debug(f"Creating reaction for shout: {shout_dict['id']}")
rdict = await _create_reaction(session, info, shout, author_id, reaction) rdict = await _create_reaction(session, info, shout, author_id, reaction)
logger.debug(f"Created reaction result: {rdict}") logger.debug(f"Created reaction result: {rdict}")