This commit is contained in:
parent
391958e60c
commit
2eaefaa62f
4
main.py
4
main.py
|
@ -16,15 +16,13 @@ async def start_up():
|
||||||
if MODE == "dev":
|
if MODE == "dev":
|
||||||
if exists(DEV_SERVER_PID_FILE_NAME):
|
if exists(DEV_SERVER_PID_FILE_NAME):
|
||||||
await redis.connect()
|
await redis.connect()
|
||||||
return
|
|
||||||
else:
|
else:
|
||||||
with open(DEV_SERVER_PID_FILE_NAME, "w", encoding="utf-8") as f:
|
with open(DEV_SERVER_PID_FILE_NAME, "w", encoding="utf-8") as f:
|
||||||
f.write(str(os.getpid()))
|
f.write(str(os.getpid()))
|
||||||
else:
|
else:
|
||||||
await redis.connect()
|
await redis.connect()
|
||||||
notification_service_task = asyncio.create_task(reactions_worker())
|
notification_service_task = asyncio.create_task(reactions_worker())
|
||||||
print(f"[main] {notification_service_task}")
|
print(f"[main.start_up] {notification_service_task}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import sentry_sdk
|
import sentry_sdk
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
from enum import Enum as Enumeration
|
from enum import Enum as Enumeration
|
||||||
import time
|
import time
|
||||||
from sqlalchemy import Boolean, Column, Enum, Integer
|
from sqlalchemy import Boolean, Column, Enum, Integer, ForeignKey, JSON as JSONType
|
||||||
from sqlalchemy.dialects.postgresql import JSONB
|
from sqlalchemy.orm import relationship
|
||||||
|
# from sqlalchemy.dialects.postgresql import JSONB
|
||||||
|
from orm.author import Author
|
||||||
from services.db import Base
|
from services.db import Base
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,12 +22,19 @@ class NotificationAction(Enumeration):
|
||||||
UNFOLLOW = 6
|
UNFOLLOW = 6
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationSeen(Base):
|
||||||
|
__tablename__ = "notification_seen"
|
||||||
|
|
||||||
|
viewer = Column(ForeignKey("author.id"))
|
||||||
|
notification = Column(ForeignKey("notification.id"))
|
||||||
|
|
||||||
|
|
||||||
class Notification(Base):
|
class Notification(Base):
|
||||||
__tablename__ = "notification"
|
__tablename__ = "notification"
|
||||||
|
|
||||||
created_at = Column(Integer, default=lambda: int(time.time()))
|
created_at = Column(Integer, default=lambda: int(time.time()))
|
||||||
seen = Column(Boolean, nullable=False, default=False, index=True)
|
|
||||||
entity = Column(Enum(NotificationEntity), nullable=False)
|
entity = Column(Enum(NotificationEntity), nullable=False)
|
||||||
action = Column(Enum(NotificationAction), nullable=False)
|
action = Column(Enum(NotificationAction), nullable=False)
|
||||||
payload = Column(JSONB, nullable=True)
|
payload = Column(JSONType, nullable=True)
|
||||||
# occurrences = Column(Integer, default=1)
|
|
||||||
|
seen = relationship(lambda: Author, secondary="notification_seen")
|
||||||
|
|
|
@ -17,11 +17,13 @@ redis = {extras = ["hiredis"], version = "^5.0.1"}
|
||||||
uvicorn = "^0.24.0.post1"
|
uvicorn = "^0.24.0.post1"
|
||||||
strawberry-graphql = {extras = ["asgi", "debug-server"], version = "^0.215.1" }
|
strawberry-graphql = {extras = ["asgi", "debug-server"], version = "^0.215.1" }
|
||||||
sentry-sdk = "^1.37.1"
|
sentry-sdk = "^1.37.1"
|
||||||
|
strawberry-sqlalchemy-mapper = "^0.3.1"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
pytest = "^7.4.2"
|
pytest = "^7.4.2"
|
||||||
black = { version = "^23.9.1", python = ">=3.12" }
|
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"
|
||||||
|
|
||||||
[tool.black]
|
[tool.black]
|
||||||
line-length = 120
|
line-length = 120
|
||||||
|
|
|
@ -1,27 +1,25 @@
|
||||||
from typing import List
|
from typing import List, Any
|
||||||
|
|
||||||
import strawberry
|
|
||||||
from sqlalchemy import and_
|
from sqlalchemy import and_
|
||||||
|
from sqlalchemy.orm import aliased
|
||||||
from orm.author import Author
|
from orm.author import Author
|
||||||
|
from orm.notification import Notification as NotificationMessage, NotificationSeen
|
||||||
from services.auth import login_required
|
from services.auth import login_required
|
||||||
from services.db import local_session
|
from services.db import local_session
|
||||||
|
from strawberry_sqlalchemy_mapper import StrawberrySQLAlchemyMapper
|
||||||
|
import strawberry
|
||||||
|
|
||||||
|
strawberry_sqlalchemy_mapper = StrawberrySQLAlchemyMapper()
|
||||||
|
|
||||||
|
|
||||||
@strawberry.type
|
@strawberry_sqlalchemy_mapper.type(NotificationMessage)
|
||||||
class NotificationSeen:
|
|
||||||
notification: int # notification id
|
|
||||||
viewer: int # author id
|
|
||||||
|
|
||||||
|
|
||||||
@strawberry.type
|
|
||||||
class Notification:
|
class Notification:
|
||||||
id: int
|
id: int
|
||||||
action: str # create update delete join follow etc.
|
action: str # create update delete join follow etc.
|
||||||
entity: str # REACTION SHOUT
|
entity: str # REACTION SHOUT
|
||||||
created_at: int
|
created_at: int
|
||||||
seen: list[NotificationSeen]
|
payload: str # JSON data
|
||||||
data: str # JSON data
|
seen: List[int]
|
||||||
occurrences: int
|
|
||||||
|
|
||||||
|
|
||||||
@strawberry.type
|
@strawberry.type
|
||||||
|
@ -35,14 +33,6 @@ class NotificationsResult:
|
||||||
unread: int
|
unread: int
|
||||||
total: int
|
total: int
|
||||||
|
|
||||||
def notification_seen_by_viewer(viewer_id, notification_id, session):
|
|
||||||
seen = (
|
|
||||||
session.query(NotificationSeen)
|
|
||||||
.filter(NotificationSeen.viewer == viewer_id, NotificationSeen.notification == notification_id)
|
|
||||||
.first()
|
|
||||||
)
|
|
||||||
return seen is not None
|
|
||||||
|
|
||||||
|
|
||||||
@strawberry.type
|
@strawberry.type
|
||||||
class Query:
|
class Query:
|
||||||
|
@ -50,25 +40,32 @@ class Query:
|
||||||
@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"]
|
user_id = info.context["user_id"]
|
||||||
|
nr = NotificationsResult()
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
author = session.query(Author).filter(Author.user == user_id).first()
|
author = session.query(Author).filter(Author.user == user_id).first()
|
||||||
|
NotificationSeenAlias = aliased(NotificationSeen)
|
||||||
|
|
||||||
query = (
|
if author:
|
||||||
session.query(Notification)
|
nnn = session.query(
|
||||||
.outerjoin(
|
NotificationMessage,
|
||||||
|
NotificationSeenAlias.viewer.label("seen")
|
||||||
|
).outerjoin(
|
||||||
NotificationSeen,
|
NotificationSeen,
|
||||||
and_(NotificationSeen.viewer == author.id, NotificationSeen.notification == Notification.id),
|
and_(NotificationSeen.viewer == author.id, NotificationSeen.notification == NotificationMessage.id),
|
||||||
)
|
).limit(limit).offset(offset).all()
|
||||||
.limit(limit)
|
nr.notifications = []
|
||||||
.offset(offset)
|
for n in nnn:
|
||||||
)
|
ntf = Notification()
|
||||||
|
ntf.id = n.id
|
||||||
nslist = query.all()
|
ntf.payload = n.payload
|
||||||
total = query.group_by(Notification.id).count()
|
ntf.entity = n.entity
|
||||||
unread = sum(1 for n in nslist if not n.seen_by_viewer)
|
ntf.action = n.action
|
||||||
|
ntf.created_at = n.created_at
|
||||||
return NotificationsResult(notifications=nslist, unread=unread, total=total)
|
ntf.seen = n.seen
|
||||||
|
nr.notifications.append(ntf)
|
||||||
|
nr.unread = sum(1 for n in nr.notifications if author.id in n.seen)
|
||||||
|
nr.total = session.query(NotificationMessage).count()
|
||||||
|
return nr
|
||||||
|
|
||||||
|
|
||||||
@strawberry.type
|
@strawberry.type
|
||||||
|
@ -77,8 +74,8 @@ class 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"]
|
user_id = info.context["user_id"]
|
||||||
try:
|
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
|
try:
|
||||||
author = session.query(Author).filter(Author.user == user_id).first()
|
author = session.query(Author).filter(Author.user == user_id).first()
|
||||||
ns = NotificationSeen({"notification": notification_id, "viewer": author.id})
|
ns = NotificationSeen({"notification": notification_id, "viewer": author.id})
|
||||||
session.add(ns)
|
session.add(ns)
|
||||||
|
@ -86,7 +83,9 @@ class Mutation:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
session.rollback()
|
session.rollback()
|
||||||
print(f"[mark_notification_as_read] error: {str(e)}")
|
print(f"[mark_notification_as_read] error: {str(e)}")
|
||||||
return NotificationSeenResult(error="cant mark as read")
|
nsr = NotificationSeenResult()
|
||||||
|
nsr.error = "cant mark as read"
|
||||||
|
return nsr
|
||||||
return NotificationSeenResult()
|
return NotificationSeenResult()
|
||||||
|
|
||||||
@strawberry.mutation
|
@strawberry.mutation
|
||||||
|
@ -97,11 +96,14 @@ class Mutation:
|
||||||
with local_session() as session:
|
with local_session() as session:
|
||||||
try:
|
try:
|
||||||
author = session.query(Author).filter(Author.user == user_id).first()
|
author = session.query(Author).filter(Author.user == user_id).first()
|
||||||
_nslist = session.quuery(NotificationSeen).filter(NotificationSeen.viewer == author.id).all()
|
if author:
|
||||||
|
_nslist = session.query(NotificationSeen).filter(NotificationSeen.viewer == author.id).all()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
session.rollback()
|
session.rollback()
|
||||||
print(f"[mark_all_notifications_as_read] error: {str(e)}")
|
print(f"[mark_all_notifications_as_read] error: {str(e)}")
|
||||||
return NotificationSeenResult(error="cant mark as read")
|
nsr = NotificationSeenResult()
|
||||||
|
nsr.error = "cant mark as read"
|
||||||
|
return nsr
|
||||||
return NotificationSeenResult()
|
return NotificationSeenResult()
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user