This commit is contained in:
parent
2d89a4ec86
commit
68fce283bc
|
@ -1,25 +0,0 @@
|
||||||
import time
|
|
||||||
|
|
||||||
from sqlalchemy import Column, ForeignKey, Integer, String
|
|
||||||
|
|
||||||
from services.db import Base
|
|
||||||
|
|
||||||
|
|
||||||
class ShoutCollection(Base):
|
|
||||||
__tablename__ = "shout_collection"
|
|
||||||
|
|
||||||
id = None # type: ignore
|
|
||||||
shout = Column(ForeignKey("shout.id"), primary_key=True)
|
|
||||||
collection = Column(ForeignKey("collection.id"), primary_key=True)
|
|
||||||
|
|
||||||
|
|
||||||
class Collection(Base):
|
|
||||||
__tablename__ = "collection"
|
|
||||||
|
|
||||||
slug = Column(String, unique=True)
|
|
||||||
title = Column(String, nullable=False, comment="Title")
|
|
||||||
body = Column(String, nullable=True, comment="Body")
|
|
||||||
pic = Column(String, nullable=True, comment="Picture")
|
|
||||||
created_at = Column(Integer, default=lambda: int(time.time()))
|
|
||||||
created_by = Column(ForeignKey("author.id"), comment="Created By")
|
|
||||||
publishedAt = Column(Integer, default=lambda: int(time.time()))
|
|
|
@ -1,41 +0,0 @@
|
||||||
import time
|
|
||||||
|
|
||||||
from sqlalchemy import Column, ForeignKey, Integer, String
|
|
||||||
from sqlalchemy.orm import relationship
|
|
||||||
|
|
||||||
from orm.author import Author
|
|
||||||
from services.db import Base, local_session
|
|
||||||
|
|
||||||
|
|
||||||
class CommunityAuthor(Base):
|
|
||||||
__tablename__ = "community_author"
|
|
||||||
|
|
||||||
id = None # type: ignore
|
|
||||||
follower = Column(ForeignKey("author.id"), primary_key=True)
|
|
||||||
community = Column(ForeignKey("community.id"), primary_key=True)
|
|
||||||
joined_at = Column(Integer, nullable=False, default=lambda: int(time.time()))
|
|
||||||
role = Column(String, nullable=False)
|
|
||||||
|
|
||||||
|
|
||||||
class Community(Base):
|
|
||||||
__tablename__ = "community"
|
|
||||||
|
|
||||||
name = Column(String, nullable=False)
|
|
||||||
slug = Column(String, nullable=False, unique=True)
|
|
||||||
desc = Column(String, nullable=False, default="")
|
|
||||||
pic = Column(String, nullable=False, default="")
|
|
||||||
created_at = Column(Integer, nullable=False, default=lambda: int(time.time()))
|
|
||||||
|
|
||||||
authors = relationship(lambda: Author, secondary=CommunityAuthor.__tablename__)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def init_table():
|
|
||||||
with local_session("orm.community") as session:
|
|
||||||
d = session.query(Community).filter(Community.slug == "discours").first()
|
|
||||||
if not d:
|
|
||||||
d = Community(name="Дискурс", slug="discours")
|
|
||||||
session.add(d)
|
|
||||||
session.commit()
|
|
||||||
print("[orm.community] created community %s" % d.slug)
|
|
||||||
Community.default_community = d
|
|
||||||
print("[orm.community] default community is %s" % d.slug)
|
|
|
@ -1,43 +0,0 @@
|
||||||
import time
|
|
||||||
from enum import Enum as Enumeration
|
|
||||||
|
|
||||||
from sqlalchemy import Column, Enum, ForeignKey, Integer, String
|
|
||||||
|
|
||||||
from services.db import Base
|
|
||||||
|
|
||||||
|
|
||||||
class ReactionKind(Enumeration):
|
|
||||||
# TYPE = <reaction index> # rating diff
|
|
||||||
|
|
||||||
# editor mode
|
|
||||||
AGREE = 1 # +1
|
|
||||||
DISAGREE = 2 # -1
|
|
||||||
ASK = 3 # +0
|
|
||||||
PROPOSE = 4 # +0
|
|
||||||
PROOF = 5 # +1
|
|
||||||
DISPROOF = 6 # -1
|
|
||||||
ACCEPT = 7 # +1
|
|
||||||
REJECT = 8 # -1
|
|
||||||
|
|
||||||
# public feed
|
|
||||||
QUOTE = 9 # +0
|
|
||||||
COMMENT = 0 # +0
|
|
||||||
LIKE = 11 # +1
|
|
||||||
DISLIKE = 12 # -1
|
|
||||||
|
|
||||||
|
|
||||||
class Reaction(Base):
|
|
||||||
__tablename__ = "reaction"
|
|
||||||
|
|
||||||
body = Column(String, default='', comment="Reaction Body")
|
|
||||||
created_at = Column(Integer, nullable=False, default=lambda: int(time.time()))
|
|
||||||
created_by = Column(ForeignKey("author.id"), nullable=False, index=True)
|
|
||||||
updated_at = Column(Integer, nullable=True, comment="Updated at")
|
|
||||||
deleted_at = Column(Integer, nullable=True, comment="Deleted at")
|
|
||||||
deleted_by = Column(ForeignKey("author.id"), nullable=True, index=True)
|
|
||||||
shout = Column(ForeignKey("shout.id"), nullable=False, index=True)
|
|
||||||
reply_to = Column(ForeignKey("reaction.id"), nullable=True)
|
|
||||||
quote = Column(String, nullable=True, comment="Original quoted text")
|
|
||||||
kind = Column(Enum(ReactionKind), nullable=False)
|
|
||||||
|
|
||||||
oid = Column(String)
|
|
86
orm/shout.py
86
orm/shout.py
|
@ -1,86 +0,0 @@
|
||||||
import time
|
|
||||||
from enum import Enum as Enumeration
|
|
||||||
|
|
||||||
from sqlalchemy import JSON, Boolean, Column, Enum, ForeignKey, Integer, String
|
|
||||||
from sqlalchemy.orm import relationship
|
|
||||||
|
|
||||||
from orm.author import Author
|
|
||||||
from orm.community import Community
|
|
||||||
from orm.reaction import Reaction
|
|
||||||
from orm.topic import Topic
|
|
||||||
from services.db import Base
|
|
||||||
|
|
||||||
|
|
||||||
class ShoutTopic(Base):
|
|
||||||
__tablename__ = "shout_topic"
|
|
||||||
|
|
||||||
id = None # type: ignore
|
|
||||||
shout = Column(ForeignKey("shout.id"), primary_key=True, index=True)
|
|
||||||
topic = Column(ForeignKey("topic.id"), primary_key=True, index=True)
|
|
||||||
|
|
||||||
|
|
||||||
class ShoutReactionsFollower(Base):
|
|
||||||
__tablename__ = "shout_reactions_followers"
|
|
||||||
|
|
||||||
id = None # type: ignore
|
|
||||||
follower = Column(ForeignKey("author.id"), primary_key=True, index=True)
|
|
||||||
shout = Column(ForeignKey("shout.id"), primary_key=True, index=True)
|
|
||||||
auto = Column(Boolean, nullable=False, default=False)
|
|
||||||
created_at = Column(Integer, nullable=False, default=lambda: int(time.time()))
|
|
||||||
deleted_at = Column(Integer, nullable=True)
|
|
||||||
|
|
||||||
|
|
||||||
class ShoutAuthor(Base):
|
|
||||||
__tablename__ = "shout_author"
|
|
||||||
|
|
||||||
id = None # type: ignore
|
|
||||||
shout = Column(ForeignKey("shout.id"), primary_key=True, index=True)
|
|
||||||
author = Column(ForeignKey("author.id"), primary_key=True, index=True)
|
|
||||||
caption = Column(String, nullable=True, default="")
|
|
||||||
|
|
||||||
|
|
||||||
class ShoutCommunity(Base):
|
|
||||||
__tablename__ = "shout_community"
|
|
||||||
|
|
||||||
id = None # type: ignore
|
|
||||||
shout = Column(ForeignKey("shout.id"), primary_key=True, index=True)
|
|
||||||
community = Column(ForeignKey("community.id"), primary_key=True, index=True)
|
|
||||||
|
|
||||||
|
|
||||||
class ShoutVisibility(Enumeration):
|
|
||||||
AUTHORS = 0
|
|
||||||
COMMUNITY = 1
|
|
||||||
PUBLIC = 2
|
|
||||||
|
|
||||||
|
|
||||||
class Shout(Base):
|
|
||||||
__tablename__ = "shout"
|
|
||||||
|
|
||||||
created_at = Column(Integer, nullable=False, default=lambda: int(time.time()))
|
|
||||||
updated_at = Column(Integer, nullable=True)
|
|
||||||
published_at = Column(Integer, nullable=True)
|
|
||||||
deleted_at = Column(Integer, nullable=True)
|
|
||||||
|
|
||||||
deleted_by = Column(ForeignKey("author.id"), nullable=True)
|
|
||||||
|
|
||||||
body = Column(String, nullable=False, comment="Body")
|
|
||||||
slug = Column(String, unique=True)
|
|
||||||
cover = Column(String, nullable=True, comment="Cover image url")
|
|
||||||
lead = Column(String, nullable=True)
|
|
||||||
description = Column(String, nullable=True)
|
|
||||||
title = Column(String, nullable=True)
|
|
||||||
subtitle = Column(String, nullable=True)
|
|
||||||
layout = Column(String, nullable=True)
|
|
||||||
media = Column(JSON, nullable=True)
|
|
||||||
|
|
||||||
authors = relationship(lambda: Author, secondary="shout_author")
|
|
||||||
topics = relationship(lambda: Topic, secondary="shout_topic")
|
|
||||||
communities = relationship(lambda: Community, secondary="shout_community")
|
|
||||||
reactions = relationship(lambda: Reaction)
|
|
||||||
|
|
||||||
visibility = Column(Enum(ShoutVisibility), default=ShoutVisibility.AUTHORS)
|
|
||||||
|
|
||||||
lang = Column(String, nullable=False, default="ru", comment="Language")
|
|
||||||
version_of = Column(ForeignKey("shout.id"), nullable=True)
|
|
||||||
oid = Column(String, nullable=True)
|
|
||||||
|
|
26
orm/topic.py
26
orm/topic.py
|
@ -1,26 +0,0 @@
|
||||||
import time
|
|
||||||
|
|
||||||
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
|
|
||||||
|
|
||||||
from services.db import Base
|
|
||||||
|
|
||||||
|
|
||||||
class TopicFollower(Base):
|
|
||||||
__tablename__ = "topic_followers"
|
|
||||||
|
|
||||||
id = None # type: ignore
|
|
||||||
follower = Column(ForeignKey("author.id"), primary_key=True, index=True)
|
|
||||||
topic = Column(ForeignKey("topic.id"), primary_key=True, index=True)
|
|
||||||
created_at = Column(Integer, nullable=False, default=lambda: int(time.time()))
|
|
||||||
auto = Column(Boolean, nullable=False, default=False)
|
|
||||||
|
|
||||||
|
|
||||||
class Topic(Base):
|
|
||||||
__tablename__ = "topic"
|
|
||||||
|
|
||||||
slug = Column(String, unique=True)
|
|
||||||
title = Column(String, nullable=False, comment="Title")
|
|
||||||
body = Column(String, nullable=True, comment="Body")
|
|
||||||
pic = Column(String, nullable=True, comment="Picture")
|
|
||||||
community = Column(ForeignKey("community.id"), default=1)
|
|
||||||
oid = Column(String, nullable=True, comment="Old ID")
|
|
|
@ -25,6 +25,9 @@ black = { version = "^23.9.1", python = ">=3.12" }
|
||||||
mypy = { version = "^1.7", python = ">=3.12" }
|
mypy = { version = "^1.7", python = ">=3.12" }
|
||||||
setuptools = "^69.0.2"
|
setuptools = "^69.0.2"
|
||||||
|
|
||||||
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
setuptools = "^69.0.2"
|
||||||
|
|
||||||
[tool.black]
|
[tool.black]
|
||||||
line-length = 120
|
line-length = 120
|
||||||
target-version = ['py312']
|
target-version = ['py312']
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
import json
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
from orm.notification import Notification
|
from orm.notification import Notification
|
||||||
from services.db import local_session
|
from services.db import local_session
|
||||||
from services.rediscache import redis
|
from services.rediscache import redis
|
||||||
|
|
|
@ -35,15 +35,16 @@ class NotificationsResult:
|
||||||
total: int
|
total: int
|
||||||
|
|
||||||
|
|
||||||
def get_notifications(author, session, limit, offset) -> List[Notification]:
|
def get_notifications(author_id, session, limit, offset) -> List[Notification]:
|
||||||
NotificationSeenAlias = aliased(NotificationSeen)
|
NotificationSeenAlias = aliased(NotificationSeen)
|
||||||
query = select(
|
query = (
|
||||||
NotificationMessage,
|
select(NotificationMessage, NotificationSeenAlias.viewer.label("seen"))
|
||||||
NotificationSeenAlias.viewer.label("seen")
|
.outerjoin(
|
||||||
).outerjoin(
|
|
||||||
NotificationSeen,
|
NotificationSeen,
|
||||||
and_(NotificationSeen.viewer == author.id, NotificationSeen.notification == NotificationMessage.id),
|
and_(NotificationSeen.viewer == author_id, NotificationSeen.notification == NotificationMessage.id),
|
||||||
).group_by(NotificationSeen.notification)
|
)
|
||||||
|
.group_by(NotificationSeen.notification)
|
||||||
|
)
|
||||||
if limit:
|
if limit:
|
||||||
query = query.limit(limit)
|
query = query.limit(limit)
|
||||||
if offset:
|
if offset:
|
||||||
|
@ -69,26 +70,21 @@ class Query:
|
||||||
@strawberry.field
|
@strawberry.field
|
||||||
@login_required
|
@login_required
|
||||||
async def load_notifications(self, info, limit: int = 50, offset: int = 0) -> NotificationsResult:
|
async def load_notifications(self, info, limit: int = 50, offset: int = 0) -> NotificationsResult:
|
||||||
user_id = info.context["user_id"]
|
author_id = info.context.get("author_id")
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
try:
|
try:
|
||||||
author = session.query(Author).filter(Author.user == user_id).first()
|
if author_id:
|
||||||
if author:
|
notifications = get_notifications(author_id, session, limit, offset)
|
||||||
notifications = get_notifications(author, session, limit, offset)
|
|
||||||
if notifications and len(notifications) > 0:
|
if notifications and len(notifications) > 0:
|
||||||
nr = NotificationsResult(
|
nr = NotificationsResult(
|
||||||
notifications=notifications,
|
notifications=notifications,
|
||||||
unread=sum(1 for n in notifications if author.id in n.seen),
|
unread=sum(1 for n in notifications if author_id in n.seen),
|
||||||
total=session.query(NotificationMessage).count()
|
total=session.query(NotificationMessage).count(),
|
||||||
)
|
)
|
||||||
return nr
|
return nr
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print(f"[resolvers.schema] {ex}")
|
print(f"[resolvers.schema] {ex}")
|
||||||
return NotificationsResult(
|
return NotificationsResult(notifications=[], total=0, unread=0)
|
||||||
notifications=[],
|
|
||||||
total=0,
|
|
||||||
unread=0
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@strawberry.type
|
@strawberry.type
|
||||||
|
@ -96,12 +92,11 @@ class Mutation:
|
||||||
@strawberry.mutation
|
@strawberry.mutation
|
||||||
@login_required
|
@login_required
|
||||||
async def mark_notification_as_read(self, info, notification_id: int) -> NotificationSeenResult:
|
async def mark_notification_as_read(self, info, notification_id: int) -> NotificationSeenResult:
|
||||||
user_id = info.context["user_id"]
|
author_id = info.context.get("author_id")
|
||||||
|
if author_id:
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
try:
|
try:
|
||||||
author = session.query(Author).filter(Author.user == user_id).first()
|
ns = NotificationSeen(notification=notification_id, viewer=author_id)
|
||||||
if author:
|
|
||||||
ns = NotificationSeen(notification=notification_id, viewer=author.id)
|
|
||||||
session.add(ns)
|
session.add(ns)
|
||||||
session.commit()
|
session.commit()
|
||||||
except SQLAlchemyError as e:
|
except SQLAlchemyError as e:
|
||||||
|
@ -114,16 +109,14 @@ class Mutation:
|
||||||
@strawberry.mutation
|
@strawberry.mutation
|
||||||
@login_required
|
@login_required
|
||||||
async def mark_all_notifications_as_read(self, info) -> NotificationSeenResult:
|
async def mark_all_notifications_as_read(self, info) -> NotificationSeenResult:
|
||||||
user_id = info.context["user_id"]
|
author_id = info.context.get("author_id")
|
||||||
|
if author_id:
|
||||||
with local_session() as session:
|
|
||||||
try:
|
try:
|
||||||
author = session.query(Author).filter(Author.user == user_id).first()
|
with local_session() as session:
|
||||||
if author:
|
nslist = get_notifications(author_id, session, None, None)
|
||||||
nslist = get_notifications(author, session, None, None)
|
|
||||||
for n in nslist:
|
for n in nslist:
|
||||||
if author.id not in n.seen:
|
if author_id not in n.seen:
|
||||||
ns = NotificationSeen(viewer=author.id, notification=n.id)
|
ns = NotificationSeen(viewer=author_id, notification=n.id)
|
||||||
session.add(ns)
|
session.add(ns)
|
||||||
session.commit()
|
session.commit()
|
||||||
except SQLAlchemyError as e:
|
except SQLAlchemyError as e:
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from httpx import AsyncClient, HTTPError
|
from httpx import AsyncClient
|
||||||
|
|
||||||
|
from orm.author import Author
|
||||||
|
from services.db import local_session
|
||||||
from settings import AUTH_URL
|
from settings import AUTH_URL
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,23 +45,14 @@ def login_required(f):
|
||||||
raise Exception("You are not logged in")
|
raise Exception("You are not logged in")
|
||||||
else:
|
else:
|
||||||
# Добавляем author_id в контекст
|
# Добавляем author_id в контекст
|
||||||
|
with local_session() as session:
|
||||||
|
author = session.query(Author).filter(Author.user == user_id).first()
|
||||||
|
if author:
|
||||||
|
context["author_id"] = author.id
|
||||||
|
if user_id:
|
||||||
context["user_id"] = user_id
|
context["user_id"] = user_id
|
||||||
|
|
||||||
# Если пользователь аутентифицирован, выполняем резолвер
|
# Если пользователь аутентифицирован, выполняем резолвер
|
||||||
return await f(*args, **kwargs)
|
return await f(*args, **kwargs)
|
||||||
|
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
def auth_request(f):
|
|
||||||
@wraps(f)
|
|
||||||
async def decorated_function(*args, **kwargs):
|
|
||||||
req = args[0]
|
|
||||||
is_authenticated, user_id = await check_auth(req)
|
|
||||||
if not is_authenticated:
|
|
||||||
raise HTTPError("please, login first")
|
|
||||||
else:
|
|
||||||
req["user_id"] = user_id
|
|
||||||
return await f(*args, **kwargs)
|
|
||||||
|
|
||||||
return decorated_function
|
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
from typing import List
|
from typing import List, Any
|
||||||
|
|
||||||
from httpx import AsyncClient
|
from httpx import AsyncClient
|
||||||
|
|
||||||
from orm.author import Author
|
|
||||||
from orm.shout import Shout
|
|
||||||
from settings import API_BASE
|
from settings import API_BASE
|
||||||
|
|
||||||
headers = {"Content-Type": "application/json"}
|
headers = {"Content-Type": "application/json"}
|
||||||
|
@ -27,22 +23,7 @@ async def _request_endpoint(query_name, body):
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
async def get_author(author_id) -> Author:
|
async def get_followed_shouts(author_id: int) -> List[Any]:
|
||||||
query_name = "get_author"
|
|
||||||
query_type = "query"
|
|
||||||
operation = "GetAuthor"
|
|
||||||
query_fields = "id slug pic name"
|
|
||||||
|
|
||||||
gql = {
|
|
||||||
"query": query_type + " " + operation + " { " + query_name + " { " + query_fields + "} " + " }",
|
|
||||||
"operationName": operation,
|
|
||||||
"variables": None,
|
|
||||||
}
|
|
||||||
|
|
||||||
return await _request_endpoint(query_name, gql)
|
|
||||||
|
|
||||||
|
|
||||||
async def get_followed_shouts(author_id: int) -> List[Shout]:
|
|
||||||
query_name = "load_shouts_followed"
|
query_name = "load_shouts_followed"
|
||||||
query_type = "query"
|
query_type = "query"
|
||||||
operation = "GetFollowedShouts"
|
operation = "GetFollowedShouts"
|
||||||
|
@ -55,7 +36,7 @@ async def get_followed_shouts(author_id: int) -> List[Shout]:
|
||||||
body = {
|
body = {
|
||||||
"query": query,
|
"query": query,
|
||||||
"operationName": operation,
|
"operationName": operation,
|
||||||
"variables": {"author_id": author_id, "limit": 1000, "offset": 0}, # FIXME: too big
|
"variables": {"author_id": author_id, "limit": 1000, "offset": 0}, # FIXME: too big limit
|
||||||
}
|
}
|
||||||
|
|
||||||
return await _request_endpoint(query_name, body)
|
return await _request_endpoint(query_name, body)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user