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" }
|
||||
setuptools = "^69.0.2"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
setuptools = "^69.0.2"
|
||||
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
target-version = ['py312']
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
import json
|
||||
import asyncio
|
||||
|
||||
from orm.notification import Notification
|
||||
from services.db import local_session
|
||||
from services.rediscache import redis
|
||||
|
|
|
@ -35,15 +35,16 @@ class NotificationsResult:
|
|||
total: int
|
||||
|
||||
|
||||
def get_notifications(author, session, limit, offset) -> List[Notification]:
|
||||
def get_notifications(author_id, session, limit, offset) -> List[Notification]:
|
||||
NotificationSeenAlias = aliased(NotificationSeen)
|
||||
query = select(
|
||||
NotificationMessage,
|
||||
NotificationSeenAlias.viewer.label("seen")
|
||||
).outerjoin(
|
||||
NotificationSeen,
|
||||
and_(NotificationSeen.viewer == author.id, NotificationSeen.notification == NotificationMessage.id),
|
||||
).group_by(NotificationSeen.notification)
|
||||
query = (
|
||||
select(NotificationMessage, NotificationSeenAlias.viewer.label("seen"))
|
||||
.outerjoin(
|
||||
NotificationSeen,
|
||||
and_(NotificationSeen.viewer == author_id, NotificationSeen.notification == NotificationMessage.id),
|
||||
)
|
||||
.group_by(NotificationSeen.notification)
|
||||
)
|
||||
if limit:
|
||||
query = query.limit(limit)
|
||||
if offset:
|
||||
|
@ -69,26 +70,21 @@ class Query:
|
|||
@strawberry.field
|
||||
@login_required
|
||||
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:
|
||||
try:
|
||||
author = session.query(Author).filter(Author.user == user_id).first()
|
||||
if author:
|
||||
notifications = get_notifications(author, session, limit, offset)
|
||||
if author_id:
|
||||
notifications = get_notifications(author_id, session, limit, offset)
|
||||
if notifications and len(notifications) > 0:
|
||||
nr = NotificationsResult(
|
||||
notifications=notifications,
|
||||
unread=sum(1 for n in notifications if author.id in n.seen),
|
||||
total=session.query(NotificationMessage).count()
|
||||
unread=sum(1 for n in notifications if author_id in n.seen),
|
||||
total=session.query(NotificationMessage).count(),
|
||||
)
|
||||
return nr
|
||||
except Exception as ex:
|
||||
print(f"[resolvers.schema] {ex}")
|
||||
return NotificationsResult(
|
||||
notifications=[],
|
||||
total=0,
|
||||
unread=0
|
||||
)
|
||||
return NotificationsResult(notifications=[], total=0, unread=0)
|
||||
|
||||
|
||||
@strawberry.type
|
||||
|
@ -96,34 +92,31 @@ class Mutation:
|
|||
@strawberry.mutation
|
||||
@login_required
|
||||
async def mark_notification_as_read(self, info, notification_id: int) -> NotificationSeenResult:
|
||||
user_id = info.context["user_id"]
|
||||
with local_session() as session:
|
||||
try:
|
||||
author = session.query(Author).filter(Author.user == user_id).first()
|
||||
if author:
|
||||
ns = NotificationSeen(notification=notification_id, viewer=author.id)
|
||||
author_id = info.context.get("author_id")
|
||||
if author_id:
|
||||
with local_session() as session:
|
||||
try:
|
||||
ns = NotificationSeen(notification=notification_id, viewer=author_id)
|
||||
session.add(ns)
|
||||
session.commit()
|
||||
except SQLAlchemyError as e:
|
||||
session.rollback()
|
||||
print(f"[mark_notification_as_read] error: {str(e)}")
|
||||
nsr = NotificationSeenResult(error="cant mark as read")
|
||||
return nsr
|
||||
except SQLAlchemyError as e:
|
||||
session.rollback()
|
||||
print(f"[mark_notification_as_read] error: {str(e)}")
|
||||
nsr = NotificationSeenResult(error="cant mark as read")
|
||||
return nsr
|
||||
return NotificationSeenResult()
|
||||
|
||||
@strawberry.mutation
|
||||
@login_required
|
||||
async def mark_all_notifications_as_read(self, info) -> NotificationSeenResult:
|
||||
user_id = info.context["user_id"]
|
||||
|
||||
with local_session() as session:
|
||||
author_id = info.context.get("author_id")
|
||||
if author_id:
|
||||
try:
|
||||
author = session.query(Author).filter(Author.user == user_id).first()
|
||||
if author:
|
||||
nslist = get_notifications(author, session, None, None)
|
||||
with local_session() as session:
|
||||
nslist = get_notifications(author_id, session, None, None)
|
||||
for n in nslist:
|
||||
if author.id not in n.seen:
|
||||
ns = NotificationSeen(viewer=author.id, notification=n.id)
|
||||
if author_id not in n.seen:
|
||||
ns = NotificationSeen(viewer=author_id, notification=n.id)
|
||||
session.add(ns)
|
||||
session.commit()
|
||||
except SQLAlchemyError as e:
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
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
|
||||
|
||||
|
||||
|
@ -42,23 +45,14 @@ def login_required(f):
|
|||
raise Exception("You are not logged in")
|
||||
else:
|
||||
# Добавляем author_id в контекст
|
||||
context["user_id"] = user_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
|
||||
|
||||
# Если пользователь аутентифицирован, выполняем резолвер
|
||||
return await f(*args, **kwargs)
|
||||
|
||||
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 orm.author import Author
|
||||
from orm.shout import Shout
|
||||
from settings import API_BASE
|
||||
|
||||
headers = {"Content-Type": "application/json"}
|
||||
|
@ -27,22 +23,7 @@ async def _request_endpoint(query_name, body):
|
|||
traceback.print_exc()
|
||||
|
||||
|
||||
async def get_author(author_id) -> Author:
|
||||
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]:
|
||||
async def get_followed_shouts(author_id: int) -> List[Any]:
|
||||
query_name = "load_shouts_followed"
|
||||
query_type = "query"
|
||||
operation = "GetFollowedShouts"
|
||||
|
@ -55,7 +36,7 @@ async def get_followed_shouts(author_id: int) -> List[Shout]:
|
|||
body = {
|
||||
"query": query,
|
||||
"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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user