schema-fix
All checks were successful
deploy / deploy (push) Successful in 1m18s

This commit is contained in:
Untone 2023-11-26 21:21:14 +03:00
parent 391958e60c
commit 2eaefaa62f
4 changed files with 79 additions and 69 deletions

36
main.py
View File

@ -16,32 +16,30 @@ 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:
import sentry_sdk
try: sentry_sdk.init(
import sentry_sdk SENTRY_DSN,
enable_tracing=True,
sentry_sdk.init( integrations=[
SENTRY_DSN, StrawberryIntegration(
enable_tracing=True, # Set async_execution to True if you have
integrations=[ # at least one async resolver
StrawberryIntegration( async_execution=True
# Set async_execution to True if you have ),
# at least one async resolver ],
async_execution=True )
), except Exception as e:
], print("[sentry] init error")
) print(e)
except Exception as e:
print("[sentry] init error")
print(e)
async def shutdown(): async def shutdown():

View File

@ -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")

View File

@ -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

View File

@ -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,
NotificationSeen, NotificationSeenAlias.viewer.label("seen")
and_(NotificationSeen.viewer == author.id, NotificationSeen.notification == Notification.id), ).outerjoin(
) NotificationSeen,
.limit(limit) and_(NotificationSeen.viewer == author.id, NotificationSeen.notification == NotificationMessage.id),
.offset(offset) ).limit(limit).offset(offset).all()
) nr.notifications = []
for n in nnn:
nslist = query.all() ntf = Notification()
total = query.group_by(Notification.id).count() ntf.id = n.id
unread = sum(1 for n in nslist if not n.seen_by_viewer) ntf.payload = n.payload
ntf.entity = n.entity
return NotificationsResult(notifications=nslist, unread=unread, total=total) ntf.action = n.action
ntf.created_at = n.created_at
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,16 +74,18 @@ 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)
session.commit() session.commit()
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()