diff --git a/borrowing/tasks.py b/borrowing/tasks.py new file mode 100644 index 0000000..d31e992 --- /dev/null +++ b/borrowing/tasks.py @@ -0,0 +1,25 @@ +import datetime + +from celery import shared_task +from tg_bot.utils import send_telegram_notification + +from borrowing.models import Borrowing + + +@shared_task +def send_message(): + today = datetime.date.today() + overdue_borrowings = Borrowing.objects.filter( + expected_return_date__lte=today, + actual_return_date__isnull=True + ) + for borrowing in overdue_borrowings: + message = ( + f"📚 Borrowing Overdue Reminder ‼️\n" + f"User: {borrowing.user.username}\n" + f"Book: {borrowing.book.title}\n" + f"Borrow Date: {borrowing.borrow_date}\n" + f"Expected Return Date: {borrowing.expected_return_date}\n" + f"Overdue by: {today - borrowing.expected_return_date} days\n" + ) + send_telegram_notification.delay(message) diff --git a/core/settings.py b/core/settings.py index 9947ea0..0f351d4 100644 --- a/core/settings.py +++ b/core/settings.py @@ -14,6 +14,7 @@ from datetime import timedelta from pathlib import Path +from celery.schedules import crontab from dotenv import load_dotenv load_dotenv() @@ -49,6 +50,7 @@ "drf_spectacular", "django_filters", "debug_toolbar", + "django_celery_beat", # custom apps "book", "user", @@ -189,3 +191,9 @@ CELERY_RESULT_BACKEND = "redis://localhost:6379/0" CELERY_TIMEZONE = "Europe/Kiev" +CELERY_BEAT_SCHEDULE = { + "send_message_daily": { + "task": "borrowing.tasks.send_message", + "schedule": crontab(minute="*"), + }, +} diff --git a/poetry.lock b/poetry.lock index f463e28..9772539 100644 --- a/poetry.lock +++ b/poetry.lock @@ -322,6 +322,20 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "cron-descriptor" +version = "1.4.5" +description = "A Python library that converts cron expressions into human readable strings." +optional = false +python-versions = "*" +files = [ + {file = "cron_descriptor-1.4.5-py3-none-any.whl", hash = "sha256:736b3ae9d1a99bc3dbfc5b55b5e6e7c12031e7ba5de716625772f8b02dcd6013"}, + {file = "cron_descriptor-1.4.5.tar.gz", hash = "sha256:f51ce4ffc1d1f2816939add8524f206c376a42c87a5fca3091ce26725b3b1bca"}, +] + +[package.extras] +dev = ["polib"] + [[package]] name = "django" version = "5.1.4" @@ -342,6 +356,25 @@ tzdata = {version = "*", markers = "sys_platform == \"win32\""} argon2 = ["argon2-cffi (>=19.1.0)"] bcrypt = ["bcrypt"] +[[package]] +name = "django-celery-beat" +version = "2.7.0" +description = "Database-backed Periodic Tasks." +optional = false +python-versions = ">=3.8" +files = [ + {file = "django_celery_beat-2.7.0-py3-none-any.whl", hash = "sha256:851c680d8fbf608ca5fecd5836622beea89fa017bc2b3f94a5b8c648c32d84b1"}, + {file = "django_celery_beat-2.7.0.tar.gz", hash = "sha256:8482034925e09b698c05ad61c36ed2a8dbc436724a3fe119215193a4ca6dc967"}, +] + +[package.dependencies] +celery = ">=5.2.3,<6.0" +cron-descriptor = ">=1.2.32" +Django = ">=2.2,<5.2" +django-timezone-field = ">=5.0" +python-crontab = ">=2.3.4" +tzdata = "*" + [[package]] name = "django-debug-toolbar" version = "4.4.6" @@ -371,6 +404,20 @@ files = [ [package.dependencies] Django = ">=4.2" +[[package]] +name = "django-timezone-field" +version = "7.0" +description = "A Django app providing DB, form, and REST framework fields for zoneinfo and pytz timezone objects." +optional = false +python-versions = "<4.0,>=3.8" +files = [ + {file = "django_timezone_field-7.0-py3-none-any.whl", hash = "sha256:3232e7ecde66ba4464abb6f9e6b8cc739b914efb9b29dc2cf2eee451f7cc2acb"}, + {file = "django_timezone_field-7.0.tar.gz", hash = "sha256:aa6f4965838484317b7f08d22c0d91a53d64e7bbbd34264468ae83d4023898a7"}, +] + +[package.dependencies] +Django = ">=3.2,<6.0" + [[package]] name = "djangorestframework" version = "3.15.2" @@ -649,6 +696,7 @@ files = [ {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1"}, {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8"}, {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864"}, @@ -688,6 +736,24 @@ dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pyte docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] +[[package]] +name = "python-crontab" +version = "3.2.0" +description = "Python Crontab API" +optional = false +python-versions = "*" +files = [ + {file = "python_crontab-3.2.0-py3-none-any.whl", hash = "sha256:82cb9b6a312d41ff66fd3caf3eed7115c28c195bfb50711bc2b4b9592feb9fe5"}, + {file = "python_crontab-3.2.0.tar.gz", hash = "sha256:40067d1dd39ade3460b2ad8557c7651514cd3851deffff61c5c60e1227c5c36b"}, +] + +[package.dependencies] +python-dateutil = "*" + +[package.extras] +cron-description = ["cron-descriptor"] +cron-schedule = ["croniter"] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -1120,4 +1186,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.12.8" -content-hash = "94be5597ed1306dcdc84ad5043c06ef779155a5d2279fd75cc4e778919947c2b" +content-hash = "5f8b167b23adb24e025cabae018bcf416f97c23dd800275a5f01da5debffdc19" diff --git a/pyproject.toml b/pyproject.toml index de69b5c..c43687a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ django-debug-toolbar = "^4.4.6" stripe = "^11.4.1" celery = {extras = ["redis"], version = "^5.4.0"} python-telegram-bot = "^21.10" +django-celery-beat = "^2.7.0" [build-system] requires = ["poetry-core"]