draft2shout

This commit is contained in:
tonyrewin 2023-02-17 17:30:38 +03:00
parent 542f9e4250
commit 2b91f5a529
4 changed files with 109 additions and 44 deletions

View File

@ -13,6 +13,7 @@ class DraftTopic(Base):
id = None # type: ignore id = None # type: ignore
collab = Column(ForeignKey("draft_collab.id"), primary_key=True) collab = Column(ForeignKey("draft_collab.id"), primary_key=True)
topic = Column(ForeignKey("topic.id"), primary_key=True) topic = Column(ForeignKey("topic.id"), primary_key=True)
main = Column(Boolean, default=False)
class DraftAuthor(Base): class DraftAuthor(Base):

View File

@ -15,6 +15,7 @@ class ShoutTopic(Base):
id = None # type: ignore id = None # type: ignore
shout = Column(ForeignKey("shout.id"), primary_key=True, index=True) shout = Column(ForeignKey("shout.id"), primary_key=True, index=True)
topic = Column(ForeignKey("topic.id"), primary_key=True, index=True) topic = Column(ForeignKey("topic.id"), primary_key=True, index=True)
main = Column(Boolean, default=False)
class ShoutReactionsFollower(Base): class ShoutReactionsFollower(Base):
@ -42,28 +43,33 @@ class ShoutAuthor(Base):
class Shout(Base): class Shout(Base):
__tablename__ = "shout" __tablename__ = "shout"
slug = Column(String, unique=True) # timestamps
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)
createdAt = Column(DateTime, nullable=False, default=datetime.now, comment="Created at") createdAt = Column(DateTime, nullable=False, default=datetime.now, comment="Created at")
updatedAt = Column(DateTime, nullable=True, comment="Updated at") updatedAt = Column(DateTime, nullable=True, comment="Updated at")
publishedAt = Column(DateTime, nullable=True) publishedAt = Column(DateTime, nullable=True)
deletedAt = 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 @staticmethod
def init_table(): def init_table():
with local_session() as session: with local_session() as session:

View File

@ -6,7 +6,7 @@ from urllib.parse import quote_plus
from graphql.type import GraphQLResolveInfo from graphql.type import GraphQLResolveInfo
from starlette.responses import RedirectResponse from starlette.responses import RedirectResponse
from transliterate import translit from transliterate import translit
import re
from auth.authenticate import login_required from auth.authenticate import login_required
from auth.credentials import AuthCredentials from auth.credentials import AuthCredentials
from auth.email import send_auth_email from auth.email import send_auth_email
@ -92,6 +92,7 @@ def create_user(user_dict):
def generate_unique_slug(src): def generate_unique_slug(src):
print('[resolvers.auth] generating slug from: ' + src) print('[resolvers.auth] generating slug from: ' + src)
slug = translit(src, "ru", reversed=True).replace(".", "-").lower() slug = translit(src, "ru", reversed=True).replace(".", "-").lower()
slug = re.sub('[^0-9a-zA-Z]+', '-', slug)
if slug != src: if slug != src:
print('[resolvers.auth] translited name: ' + slug) print('[resolvers.auth] translited name: ' + slug)
c = 1 c = 1

View File

@ -2,10 +2,13 @@ from auth.authenticate import login_required
from auth.credentials import AuthCredentials from auth.credentials import AuthCredentials
from base.orm import local_session from base.orm import local_session
from base.resolvers import query, mutation from base.resolvers import query, mutation
from base.exceptions import ObjectNotExist, BaseHttpException from orm.draft import DraftCollab, DraftAuthor
from orm.draft import DraftCollab, DraftAuthor, DraftTopic
from orm.shout import Shout from orm.shout import Shout
from orm.topic import Topic
from orm.user import User from orm.user import User
from datetime import datetime, timezone
from transliterate import translit
import re
@query.field("loadDrafts") @query.field("loadDrafts")
@ -18,11 +21,11 @@ async def load_drafts(_, info):
return drafts return drafts
@mutation.field("createDraft") # TODO @mutation.field("createDraft") # TODO
@login_required @login_required
async def create_draft(_, info, draft_input): async def create_draft(_, info, draft_input):
auth: AuthCredentials = info.context["request"].auth auth: AuthCredentials = info.context["request"].auth
draft_input['createdBy'] = auth.user_id
with local_session() as session: with local_session() as session:
collab = DraftCollab.create(**draft_input) collab = DraftCollab.create(**draft_input)
session.add(collab) session.add(collab)
@ -32,52 +35,56 @@ async def create_draft(_, info, draft_input):
return {} return {}
@mutation.field("deleteDraft") # TODO @mutation.field("deleteDraft")
@login_required @login_required
async def delete_draft(_, info, draft: int = 0): async def delete_draft(_, info, draft: int = 0):
auth: AuthCredentials = info.context["request"].auth auth: AuthCredentials = info.context["request"].auth
with local_session() as session: with local_session() as session:
collab = session.query(DraftCollab).where(DraftCollab.id == draft_input.id).one() d = session.query(DraftCollab).where(DraftCollab.id == draft).one()
if auth.user_id not in s.authors: if auth.user_id not in d.authors:
# raise BaseHttpException("only owner can remove coauthors") # raise BaseHttpException("only owner can remove coauthors")
return { return {
"error": "Only authors can update a draft" "error": "Only authors can update a draft"
} }
elif not collab: elif not d:
return { return {
"error": "There is no draft with this id" "error": "There is no draft with this id"
} }
else: else:
session.delete(collab) session.delete(d)
session.commit() session.commit()
return {} return {}
@mutation.field("updateDraft") # TODO: draft input type @mutation.field("updateDraft") # TODO: draft input type
@login_required @login_required
async def update_draft(_, info, draft_input): async def update_draft(_, info, draft_input):
auth: AuthCredentials = info.context["request"].auth auth: AuthCredentials = info.context["request"].auth
with local_session() as session: with local_session() as session:
collab = session.query(DraftCollab).where(DraftCollab.id == draft_input.id).one() # raises Error when not found d = session.query(
if auth.user_id not in s.authors: 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") # raise BaseHttpException("only owner can remove coauthors")
return { return {
"error": "Only authors can update draft" "error": "Only authors can update draft"
} }
elif not s: elif not d:
return { return {
"error": "There is no draft with this id" "error": "There is no draft with this id"
} }
else: else:
draft_input["updatedAt"] = datetime.now(tz=timezone.utc) draft_input["updatedAt"] = datetime.now(tz=timezone.utc)
collab.update(draft_input) d.update(draft_input)
session.commit() session.commit()
# TODO: email notify # TODO: email notify
return {} return {}
@mutation.field("inviteAuthor") @mutation.field("inviteAuthor")
@login_required @login_required
async def invite_coauthor(_, info, author: int = 0, draft: int = 0): 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 {} 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") @mutation.field("inviteAccept")
@login_required @login_required
async def accept_coauthor(_, info, draft: int): async def accept_coauthor(_, info, draft: int):
auth: AuthCredentials = info.context["request"].auth auth: AuthCredentials = info.context["request"].auth
with local_session() as session: with local_session() as session:
# c = session.query(DraftCollab).where(DraftCollab.id == draft).one() d = 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 d:
if not a.accepted:
a.accepted = True
session.commit()
# TODO: email notify
return {}
elif a.accepted == True:
return { return {
"error": "You have accepted invite before" "error": "Draft id was not found"
} }
else: 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 { 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 {}