Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send notification content through Firebase and add an Arq scheduler #613

Merged
merged 113 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
113 commits
Select commit Hold shift + click to select a range
b6991bd
First test for scheduler
Daihecyy Nov 16, 2024
ad97e65
Fix docker files
Daihecyy Nov 16, 2024
bd61a1a
Worker redis settings configuration
Daihecyy Nov 16, 2024
e25aa96
Fixed stupid mistake
Daihecyy Nov 16, 2024
bad5a7e
Fix depency scheduler
Daihecyy Nov 16, 2024
6e80232
Denesting test function
Daihecyy Nov 16, 2024
effd99f
Slight testing changes
Daihecyy Nov 16, 2024
bfe5970
Slight testing changes
Daihecyy Nov 16, 2024
5c78773
Fixed uuid type
Daihecyy Nov 16, 2024
c6fd669
Used dependency injection
Daihecyy Nov 16, 2024
ecf8130
More logging
Daihecyy Nov 16, 2024
610bc2c
test
Daihecyy Nov 17, 2024
2475ee5
test 2
Daihecyy Nov 17, 2024
5f8516d
test 3
Daihecyy Nov 17, 2024
4676374
test 4
Daihecyy Nov 17, 2024
05b48f4
test 4 fixed
Daihecyy Nov 17, 2024
0ffda1e
test 4 fixed 2
Daihecyy Nov 17, 2024
4943520
test 5 with lock
Daihecyy Nov 17, 2024
79af08c
test 6 with enter
Daihecyy Nov 17, 2024
91625ec
Test 6
Daihecyy Nov 17, 2024
b071bf0
Test_6
Daihecyy Nov 17, 2024
f5e74d7
Working scheduler functions
Daihecyy Nov 17, 2024
58f7f17
Working scheduler functions
Daihecyy Nov 17, 2024
c899e6a
fun test
Daihecyy Nov 17, 2024
d19479b
Final working config for scheduler
Daihecyy Nov 17, 2024
5390075
First clean
foucblg Oct 26, 2024
0934b94
RAF
foucblg Nov 16, 2024
2e01c3e
Edit message content for new notif system
foucblg Nov 16, 2024
d595a0a
Test future notif
foucblg Nov 17, 2024
a847d34
SUper commit
foucblg Nov 17, 2024
616199c
Hot fix
foucblg Nov 17, 2024
c267060
redis not config
foucblg Nov 17, 2024
399597d
Circular pb
foucblg Nov 17, 2024
3734b69
Still circular issue
foucblg Nov 17, 2024
082720d
Fix circular import
foucblg Nov 17, 2024
04f257c
Fix
foucblg Nov 17, 2024
b21eedf
Trying to fix lint and format
foucblg Nov 17, 2024
0eb281b
Fix schedular import
foucblg Nov 17, 2024
9823e63
First test for scheduler
Daihecyy Nov 16, 2024
42d4ddf
Fix docker files
Daihecyy Nov 16, 2024
c85b491
Worker redis settings configuration
Daihecyy Nov 16, 2024
ce21f49
Fixed stupid mistake
Daihecyy Nov 16, 2024
6c9f3a4
Fix depency scheduler
Daihecyy Nov 16, 2024
3a196e0
Denesting test function
Daihecyy Nov 16, 2024
6d8a69d
Slight testing changes
Daihecyy Nov 16, 2024
aa48936
Slight testing changes
Daihecyy Nov 16, 2024
05f16e9
Fixed uuid type
Daihecyy Nov 16, 2024
01ad4b1
Used dependency injection
Daihecyy Nov 16, 2024
c84abe1
More logging
Daihecyy Nov 16, 2024
e77a89f
test
Daihecyy Nov 17, 2024
ebdc9b7
test 2
Daihecyy Nov 17, 2024
c5521e9
test 3
Daihecyy Nov 17, 2024
be5342e
test 4
Daihecyy Nov 17, 2024
db40d3a
test 4 fixed
Daihecyy Nov 17, 2024
9ab1f2a
test 4 fixed 2
Daihecyy Nov 17, 2024
c8a3404
test 5 with lock
Daihecyy Nov 17, 2024
906d968
test 6 with enter
Daihecyy Nov 17, 2024
b16e2b9
Test 6
Daihecyy Nov 17, 2024
959445b
Test_6
Daihecyy Nov 17, 2024
3773823
Working scheduler functions
Daihecyy Nov 17, 2024
209a02e
Working scheduler functions
Daihecyy Nov 17, 2024
ab33a7d
First clean
foucblg Oct 26, 2024
794f2d3
RAF
foucblg Nov 16, 2024
ebf8342
Edit message content for new notif system
foucblg Nov 16, 2024
12ac595
Test future notif
foucblg Nov 17, 2024
53ee3ff
SUper commit
foucblg Nov 17, 2024
5234e43
Hot fix
foucblg Nov 17, 2024
4f4612c
redis not config
foucblg Nov 17, 2024
a7ca58a
Circular pb
foucblg Nov 17, 2024
88c6978
Still circular issue
foucblg Nov 17, 2024
11c9fab
Fix circular import
foucblg Nov 17, 2024
241381c
Fix
foucblg Nov 17, 2024
aec0b97
Trying to fix lint and format
foucblg Nov 17, 2024
39c01dc
Fix schedular import
foucblg Nov 17, 2024
77e69f9
class name
foucblg Nov 17, 2024
bb9cf14
Run arq scheduler in asyncio task
armanddidierjean Nov 17, 2024
7c4068e
Don't start an arq worker
armanddidierjean Nov 17, 2024
2e7381d
Pass get_db dependency to get a db session in planned tasks
armanddidierjean Nov 18, 2024
565f3b7
Feature : working future notif
foucblg Nov 18, 2024
591f1fd
Implements endpoints
foucblg Nov 18, 2024
edc7fd4
Feat : add future and cancel notif for loan
foucblg Nov 20, 2024
7981fe3
ruff fix
foucblg Nov 25, 2024
86c38b0
Allowed for running without REDIS
Daihecyy Nov 20, 2024
80048db
Fixed type inconsistencies
Daihecyy Nov 20, 2024
4be23a9
Ruff check
foucblg Nov 25, 2024
5fef462
ruff format
foucblg Nov 25, 2024
f7bcaec
Update app/types/scheduler.py
foucblg Nov 26, 2024
40cef5c
Update app/modules/advert/endpoints_advert.py
foucblg Nov 26, 2024
c7d5f1c
Update app/types/scheduler.py
Daihecyy Nov 26, 2024
1dbc14d
Defer seconds -> int
foucblg Dec 2, 2024
949573a
Feat : unique function to send notif
foucblg Dec 2, 2024
3aebcc4
Fix avoiding n notification for cine
foucblg Dec 2, 2024
de853f8
Fix : update topic membership for new tokens
foucblg Dec 2, 2024
11c9482
lint and format grmf
foucblg Dec 2, 2024
03ce388
Revert test line
Daihecyy Dec 3, 2024
205e0d7
Fix : Cancel notification with notif manager
foucblg Dec 3, 2024
c6b36fa
Dummy Scheduler to Offlinescheduler
foucblg Dec 3, 2024
69dd104
Feat : removing scheduler time defer
foucblg Dec 3, 2024
716def3
Feat : arq logging
foucblg Dec 3, 2024
0c61eec
Fix : arq.warker log
foucblg Dec 3, 2024
0c641a6
Dependency injection in the scheduler
armanddidierjean Dec 3, 2024
ba99ad5
Lint and format
foucblg Dec 4, 2024
7eea068
Fix: rebase
Rotheem Dec 4, 2024
89eea29
Ruff
foucblg Dec 4, 2024
4ceea58
Inject db in run_task
armanddidierjean Dec 5, 2024
797932d
Don't pass db=None
armanddidierjean Dec 5, 2024
7f4f548
Add test endpoint
foucblg Dec 5, 2024
75fc340
Fix action module
foucblg Dec 5, 2024
f992303
Fix action module
foucblg Dec 5, 2024
d9611c2
Fix import
foucblg Dec 5, 2024
556f244
Check if annotation is AsyncSession
armanddidierjean Dec 5, 2024
cabe342
Fix missing action_module
foucblg Dec 6, 2024
6b40657
Fixing test
foucblg Dec 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,22 @@
from app.dependencies import (
get_db,
get_redis_client,
get_scheduler,
get_websocket_connection_manager,
init_and_get_db_engine,
)
from app.modules.module_list import module_list
from app.types.exceptions import (
ContentHTTPException,
GoogleAPIInvalidCredentialsError,
)
from app.types.exceptions import ContentHTTPException, GoogleAPIInvalidCredentialsError
from app.types.sqlalchemy import Base
from app.utils import initialization
from app.utils.redis import limiter

if TYPE_CHECKING:
import redis

from app.types.scheduler import Scheduler
from app.types.websocket import WebsocketConnectionManager

# NOTE: We can not get loggers at the top of this file like we do in other files
# as the loggers are not yet initialized

Expand Down Expand Up @@ -352,14 +353,27 @@ async def lifespan(app: FastAPI) -> AsyncGenerator:
# We expect this error to be raised if the credentials were never set before
pass

ws_manager = app.dependency_overrides.get(
ws_manager: WebsocketConnectionManager = app.dependency_overrides.get(
get_websocket_connection_manager,
get_websocket_connection_manager,
)(settings=settings)

arq_scheduler: Scheduler = app.dependency_overrides.get(
get_scheduler,
get_scheduler,
)(settings=settings)

await ws_manager.connect_broadcaster()
await arq_scheduler.start(
redis_host=settings.REDIS_HOST,
redis_port=settings.REDIS_PORT,
redis_password=settings.REDIS_PASSWORD,
_dependency_overrides=app.dependency_overrides,
)

yield
hyperion_error_logger.info("Shutting down")
await arq_scheduler.close()
await ws_manager.disconnect_broadcaster()

# Initialize app
Expand Down
4 changes: 3 additions & 1 deletion app/core/endpoints_core.py
foucblg marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
response_model=schemas_core.CoreInformation,
status_code=200,
)
async def read_information(settings: Settings = Depends(get_settings)):
async def read_information(
settings: Settings = Depends(get_settings),
):
"""
Return information about Hyperion. This endpoint can be used to check if the API is up.
"""
Expand Down
10 changes: 10 additions & 0 deletions app/core/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ def get_config_dict(self, settings: Settings):
],
"level": MINIMUM_LOG_LEVEL,
},
"scheduler": {"handlers": ["console"], "level": MINIMUM_LOG_LEVEL},
# We disable "uvicorn.access" to replace it with our custom "hyperion.access" which add custom information like the request_id
"uvicorn.access": {"handlers": []},
"uvicorn.error": {
Expand All @@ -245,6 +246,15 @@ def get_config_dict(self, settings: Settings):
"level": MINIMUM_LOG_LEVEL,
"propagate": False,
},
"arq.worker": {
"handlers": [
"console",
"file_errors",
"matrix_errors",
],
"level": MINIMUM_LOG_LEVEL,
"propagate": False,
},
},
}

Expand Down
90 changes: 0 additions & 90 deletions app/core/notification/cruds_notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,96 +9,6 @@
from app.core.notification.notification_types import CustomTopic, Topic


async def create_message(
message: models_notification.Message,
db: AsyncSession,
) -> None:
db.add(message)
try:
await db.commit()
except IntegrityError:
await db.rollback()
raise


async def create_batch_messages(
messages: list[models_notification.Message],
db: AsyncSession,
) -> None:
db.add_all(messages)
try:
await db.commit()
except IntegrityError:
await db.rollback()
raise


async def get_messages_by_firebase_token(
firebase_token: str,
db: AsyncSession,
) -> Sequence[models_notification.Message]:
result = await db.execute(
select(models_notification.Message).where(
models_notification.Message.firebase_device_token == firebase_token,
),
)
return result.scalars().all()


async def get_messages_by_context_and_firebase_tokens(
context: str,
firebase_tokens: list[str],
db: AsyncSession,
) -> Sequence[models_notification.Message]:
result = await db.execute(
select(models_notification.Message).where(
models_notification.Message.context == context,
models_notification.Message.firebase_device_token.in_(firebase_tokens),
),
)
return result.scalars().all()


async def remove_message_by_context_and_firebase_device_token(
context: str,
firebase_device_token: str,
db: AsyncSession,
):
await db.execute(
delete(models_notification.Message).where(
models_notification.Message.context == context,
models_notification.Message.firebase_device_token == firebase_device_token,
),
)
await db.commit()


async def remove_message_by_firebase_device_token(
firebase_device_token: str,
db: AsyncSession,
):
await db.execute(
delete(models_notification.Message).where(
models_notification.Message.firebase_device_token == firebase_device_token,
),
)
await db.commit()


async def remove_messages_by_context_and_firebase_device_tokens_list(
context: str,
tokens: list[str],
db: AsyncSession,
):
await db.execute(
delete(models_notification.Message).where(
models_notification.Message.context == context,
models_notification.Message.firebase_device_token.in_(tokens),
),
)
await db.commit()


async def get_firebase_devices_by_user_id(
user_id: str,
db: AsyncSession,
Expand Down
130 changes: 80 additions & 50 deletions app/core/notification/endpoints_notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
get_db,
get_notification_manager,
get_notification_tool,
get_scheduler,
is_user,
is_user_in,
)
from app.types.scheduler import Scheduler
from app.utils.communication.notifications import NotificationManager, NotificationTool

router = APIRouter(tags=["Notifications"])
Expand Down Expand Up @@ -61,6 +63,16 @@ async def register_firebase_device(
db=db,
)

user_topics = await cruds_notification.get_topic_memberships_by_user_id(
user_id=user.id,
db=db,
)
for topic in user_topics:
await notification_manager.subscribe_tokens_to_topic(
custom_topic=CustomTopic(topic.topic),
tokens=[firebase_token],
)

firebase_device = models_notification.FirebaseDevice(
user_id=user.id,
firebase_device_token=firebase_token,
Expand All @@ -84,7 +96,7 @@ async def unregister_firebase_device(
notification_manager: NotificationManager = Depends(get_notification_manager),
):
"""
Unregister a new firebase device for the user
Unregister a firebase device for the user

**The user must be authenticated to use this endpoint**
"""
Expand All @@ -95,46 +107,15 @@ async def unregister_firebase_device(
db=db,
)


@router.get(
"/notification/messages/{firebase_token}",
response_model=list[schemas_notification.Message],
status_code=200,
)
async def get_messages(
firebase_token: str,
db: AsyncSession = Depends(get_db),
# If we want to enable authentification for /messages/{firebase_token} endpoint, we may to uncomment the following line
# user: models_core.CoreUser = Depends(is_user()),
):
"""
Get all messages for a specific device from the user

**The user must be authenticated to use this endpoint**
"""
firebase_device = await cruds_notification.get_firebase_devices_by_user_id_and_firebase_token(
# If we want to enable authentification for /messages/{firebase_token} endpoint, we may to uncomment the following line
firebase_token=firebase_token,
db=db, # user_id=user.id,
)

if firebase_device is None:
raise HTTPException(
status_code=404,
detail="Device not found for user", # {user.id}"
)

messages = await cruds_notification.get_messages_by_firebase_token(
firebase_token=firebase_token,
db=db,
)

await cruds_notification.remove_message_by_firebase_device_token(
firebase_device_token=firebase_token,
user_topics = await cruds_notification.get_topic_memberships_by_user_id(
user_id=user.id,
db=db,
)

return messages
for topic in user_topics:
await notification_manager.unsubscribe_tokens_to_topic(
custom_topic=CustomTopic(topic.topic),
tokens=[firebase_token],
)


@router.post(
Expand Down Expand Up @@ -268,12 +249,9 @@ async def send_notification(
**Only admins can use this endpoint**
"""
message = schemas_notification.Message(
context="notification-test",
is_visible=True,
title="Test notification",
content="Ceci est un test de notification",
# The notification will expire in 3 days
expire_on=datetime.now(UTC) + timedelta(days=3),
action_module="test",
)
await notification_tool.send_notification_to_user(
user_id=user.id,
Expand All @@ -288,24 +266,76 @@ async def send_notification(
async def send_future_notification(
user: models_core.CoreUser = Depends(is_user_in(GroupType.admin)),
notification_tool: NotificationTool = Depends(get_notification_tool),
scheduler: Scheduler = Depends(get_scheduler),
):
"""
Send ourself a test notification.

**Only admins can use this endpoint**
"""
message = schemas_notification.Message(
context="future-notification-test",
is_visible=True,
title="Test notification future",
content="Ceci est un test de notification future",
# The notification will expire in 3 days
expire_on=datetime.now(UTC) + timedelta(days=3),
delivery_datetime=datetime.now(UTC) + timedelta(minutes=3),
action_module="test",
)
await notification_tool.send_notification_to_user(
user_id=user.id,
await notification_tool.send_notification_to_users(
user_ids=[user.id],
message=message,
defer_date=datetime.now(UTC) + timedelta(seconds=10),
scheduler=scheduler,
job_id="testtt",
)


@router.post(
"/notification/send/topic",
status_code=201,
)
async def send_notification_topic(
user: models_core.CoreUser = Depends(is_user_in(GroupType.admin)),
notification_tool: NotificationTool = Depends(get_notification_tool),
):
"""
Send ourself a test notification.

**Only admins can use this endpoint**
"""
message = schemas_notification.Message(
title="Test notification topic",
content="Ceci est un test de notification topic",
action_module="test",
)
await notification_tool.send_notification_to_topic(
custom_topic=CustomTopic.from_str("test"),
message=message,
)


@router.post(
"/notification/send/topic/future",
status_code=201,
)
async def send_future_notification_topic(
user: models_core.CoreUser = Depends(is_user_in(GroupType.admin)),
notification_tool: NotificationTool = Depends(get_notification_tool),
scheduler: Scheduler = Depends(get_scheduler),
):
"""
Send ourself a test notification.

**Only admins can use this endpoint**
"""
message = schemas_notification.Message(
title="Test notification future topic",
content="Ceci est un test de notification future topic",
action_module="test",
)
await notification_tool.send_notification_to_topic(
custom_topic=CustomTopic.from_str("test"),
message=message,
defer_date=datetime.now(UTC) + timedelta(seconds=10),
job_id="test26",
scheduler=scheduler,
)


Expand Down
1 change: 1 addition & 0 deletions app/core/notification/notification_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class Topic(str, Enum):
raffle = "raffle"
vote = "vote"
ph = "ph"
test = "test"


class CustomTopic:
Expand Down
Loading
Loading