From 2b91f5a529c09cb91d076ea7038acfa12d8c9b44 Mon Sep 17 00:00:00 2001 From: tonyrewin Date: Fri, 17 Feb 2023 17:30:38 +0300 Subject: [PATCH] draft2shout --- orm/draft.py | 1 + orm/shout.py | 40 ++++---- resolvers/auth.py | 3 +- resolvers/create/{collab.py => drafts.py} | 109 ++++++++++++++++------ 4 files changed, 109 insertions(+), 44 deletions(-) rename resolvers/create/{collab.py => drafts.py} (51%) diff --git a/orm/draft.py b/orm/draft.py index 6b97e5c3..bae7f1c0 100644 --- a/orm/draft.py +++ b/orm/draft.py @@ -13,6 +13,7 @@ class DraftTopic(Base): id = None # type: ignore collab = Column(ForeignKey("draft_collab.id"), primary_key=True) topic = Column(ForeignKey("topic.id"), primary_key=True) + main = Column(Boolean, default=False) class DraftAuthor(Base): diff --git a/orm/shout.py b/orm/shout.py index 04b0102b..acfdbe81 100644 --- a/orm/shout.py +++ b/orm/shout.py @@ -15,6 +15,7 @@ class ShoutTopic(Base): id = None # type: ignore shout = Column(ForeignKey("shout.id"), primary_key=True, index=True) topic = Column(ForeignKey("topic.id"), primary_key=True, index=True) + main = Column(Boolean, default=False) class ShoutReactionsFollower(Base): @@ -42,28 +43,33 @@ class ShoutAuthor(Base): class Shout(Base): __tablename__ = "shout" - slug = Column(String, unique=True) - community = Column(ForeignKey("community.id"), default=1) - lang = Column(String, nullable=False, default='ru', comment="Language") - body = Column(String, nullable=False, comment="Body") - title = Column(String, nullable=True) - subtitle = Column(String, nullable=True) - layout = Column(String, nullable=True) - mainTopic = Column(ForeignKey("topic.slug"), nullable=True) - cover = Column(String, nullable=True, comment="Cover") - authors = relationship(lambda: User, secondary=ShoutAuthor.__tablename__) - topics = relationship(lambda: Topic, secondary=ShoutTopic.__tablename__) - reactions = relationship(lambda: Reaction) - visibility = Column(String, nullable=True) # owner authors community public - versionOf = Column(ForeignKey("shout.id"), nullable=True) - oid = Column(String, nullable=True) - media = Column(JSON, nullable=True) - + # timestamps createdAt = Column(DateTime, nullable=False, default=datetime.now, comment="Created at") updatedAt = Column(DateTime, nullable=True, comment="Updated at") publishedAt = Column(DateTime, nullable=True) deletedAt = Column(DateTime, nullable=True) + # same with Draft + slug = Column(String, unique=True) + cover = Column(String, nullable=True, comment="Cover") + body = Column(String, nullable=False, comment="Body") + title = Column(String, nullable=True) + subtitle = Column(String, nullable=True) + layout = Column(String, nullable=True) + media = Column(JSON, nullable=True) + authors = relationship(lambda: User, secondary=ShoutAuthor.__tablename__) + topics = relationship(lambda: Topic, secondary=ShoutTopic.__tablename__) + + reactions = relationship(lambda: Reaction) + + # TODO: these field should be used or modified + community = Column(ForeignKey("community.id"), default=1) + lang = Column(String, nullable=False, default='ru', comment="Language") + mainTopic = Column(ForeignKey("topic.slug"), nullable=True) + visibility = Column(String, nullable=True) # owner authors community public + versionOf = Column(ForeignKey("shout.id"), nullable=True) + oid = Column(String, nullable=True) + @staticmethod def init_table(): with local_session() as session: diff --git a/resolvers/auth.py b/resolvers/auth.py index 6108a827..ea42f409 100644 --- a/resolvers/auth.py +++ b/resolvers/auth.py @@ -6,7 +6,7 @@ from urllib.parse import quote_plus from graphql.type import GraphQLResolveInfo from starlette.responses import RedirectResponse from transliterate import translit - +import re from auth.authenticate import login_required from auth.credentials import AuthCredentials from auth.email import send_auth_email @@ -92,6 +92,7 @@ def create_user(user_dict): def generate_unique_slug(src): print('[resolvers.auth] generating slug from: ' + src) slug = translit(src, "ru", reversed=True).replace(".", "-").lower() + slug = re.sub('[^0-9a-zA-Z]+', '-', slug) if slug != src: print('[resolvers.auth] translited name: ' + slug) c = 1 diff --git a/resolvers/create/collab.py b/resolvers/create/drafts.py similarity index 51% rename from resolvers/create/collab.py rename to resolvers/create/drafts.py index f8b1c8a7..de33481e 100644 --- a/resolvers/create/collab.py +++ b/resolvers/create/drafts.py @@ -2,10 +2,13 @@ from auth.authenticate import login_required from auth.credentials import AuthCredentials from base.orm import local_session from base.resolvers import query, mutation -from base.exceptions import ObjectNotExist, BaseHttpException -from orm.draft import DraftCollab, DraftAuthor, DraftTopic +from orm.draft import DraftCollab, DraftAuthor from orm.shout import Shout +from orm.topic import Topic from orm.user import User +from datetime import datetime, timezone +from transliterate import translit +import re @query.field("loadDrafts") @@ -18,11 +21,11 @@ async def load_drafts(_, info): return drafts -@mutation.field("createDraft") # TODO +@mutation.field("createDraft") # TODO @login_required async def create_draft(_, info, draft_input): auth: AuthCredentials = info.context["request"].auth - + draft_input['createdBy'] = auth.user_id with local_session() as session: collab = DraftCollab.create(**draft_input) session.add(collab) @@ -32,52 +35,56 @@ async def create_draft(_, info, draft_input): return {} -@mutation.field("deleteDraft") # TODO +@mutation.field("deleteDraft") @login_required async def delete_draft(_, info, draft: int = 0): auth: AuthCredentials = info.context["request"].auth - with local_session() as session: - collab = session.query(DraftCollab).where(DraftCollab.id == draft_input.id).one() - if auth.user_id not in s.authors: + d = session.query(DraftCollab).where(DraftCollab.id == draft).one() + if auth.user_id not in d.authors: # raise BaseHttpException("only owner can remove coauthors") return { "error": "Only authors can update a draft" } - elif not collab: + elif not d: return { "error": "There is no draft with this id" } else: - session.delete(collab) + session.delete(d) session.commit() return {} -@mutation.field("updateDraft") # TODO: draft input type +@mutation.field("updateDraft") # TODO: draft input type @login_required async def update_draft(_, info, draft_input): auth: AuthCredentials = info.context["request"].auth with local_session() as session: - collab = session.query(DraftCollab).where(DraftCollab.id == draft_input.id).one() # raises Error when not found - if auth.user_id not in s.authors: + d = session.query( + DraftCollab + ).where( + DraftCollab.id == draft_input.id + ).one() # raises Error when not found + if auth.user_id not in d.authors: # raise BaseHttpException("only owner can remove coauthors") return { "error": "Only authors can update draft" } - elif not s: + elif not d: return { "error": "There is no draft with this id" } else: draft_input["updatedAt"] = datetime.now(tz=timezone.utc) - collab.update(draft_input) + d.update(draft_input) session.commit() # TODO: email notify return {} + @mutation.field("inviteAuthor") @login_required async def invite_coauthor(_, info, author: int = 0, draft: int = 0): @@ -108,25 +115,75 @@ async def invite_coauthor(_, info, author: int = 0, draft: int = 0): return {} +def get_slug(src): + slug = translit(src, "ru", reversed=True).replace(".", "-").lower() + slug = re.sub('[^0-9a-zA-Z]+', '-', slug) + return slug + + @mutation.field("inviteAccept") @login_required async def accept_coauthor(_, info, draft: int): auth: AuthCredentials = info.context["request"].auth with local_session() as session: - # c = session.query(DraftCollab).where(DraftCollab.id == draft).one() - a = session.query(DraftAuthor).where(DraftAuthor.collab == draft).filter(DraftAuthor.author == auth.user_id).one() - if not a.accepted: - a.accepted = True - session.commit() - # TODO: email notify - return {} - elif a.accepted == True: + d = session.query(DraftCollab).where(DraftCollab.id == draft).one() + if not d: return { - "error": "You have accepted invite before" + "error": "Draft id was not found" } else: - # raise BaseHttpException("only invited can accept") + a = session.query(DraftAuthor).where(DraftAuthor.collab == draft).filter( + DraftAuthor.author == auth.user_id).one() + if not a.accepted: + a.accepted = True + session.commit() + # TODO: email notify + return {} + elif a.accepted: + return { + "error": "You have accepted invite before" + } + else: + # raise BaseHttpException("only invited can accept") + return { + "error": "You don't have an invitation yet" + } + + +@mutation.field("draftToShout") +@login_required +async def draft_to_shout(_, info, draft: int = 0): + auth: AuthCredentials = info.context["request"].auth + + with local_session() as session: + d = session.query(DraftCollab).where(DraftCollab.id == draft).one() + if auth.user_id not in d.authors: + # raise BaseHttpException("you are not in authors list") return { - "error": "You don't have an invitation yet" + "error": "You are not in authors list" } + elif d.id: + draft_authors = [a.author for a in d.authors] + draft_topics = [t.topic for t in d.topics] + authors = session.query(User).where(User.id._in(draft_authors)).all() + topics = session.query(Topic).where(Topic.id._in(draft_topics)).all() + new_shout = Shout.create({ + "authors": authors, + "body": d.body, + "title": d.title, + "subtitle": d.subtitle or "", + "topics": topics, + "media": d.media, + "slug": d.slug or get_slug(d.title), + "layout": d.layout or "article" + }) + session.add(new_shout) + session.commit() + else: + return { + "error": "Draft is not found" + } + + # TODO: email notify + return {}