From ad04604b6cf6ab5f9f4549f90174d98130fa9b7b Mon Sep 17 00:00:00 2001 From: ayxan Date: Fri, 30 Sep 2022 01:20:48 +0400 Subject: [PATCH] Added peridoic email sender with Celery --- .gitignore | 3 +- README.md | 21 ++++- requirements.txt | 22 +++++- src/config/__init__.py | 3 + src/config/celery.py | 15 ++++ src/config/settings/base.py | 5 +- src/series/tasks.py | 79 +++++++++++++++++++ .../templates/new_episodes_verification.html | 9 +++ 8 files changed, 152 insertions(+), 5 deletions(-) create mode 100644 src/config/celery.py create mode 100644 src/series/tasks.py create mode 100644 src/series/templates/new_episodes_verification.html diff --git a/.gitignore b/.gitignore index 0d512ce..d565dce 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ migrations db.sqlite3 src/config/settings/static .env -Backup-codes-series.notification.txt \ No newline at end of file +Backup-codes-series.notification.txt +celerybeat-schedule \ No newline at end of file diff --git a/README.md b/README.md index d21a16b..80eb712 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,24 @@ rename .env.example to .env
**python3 manage.py makemigrations account --settings=config.settings.development
python3 manage.py makemigrations series --settings=config.settings.development
-python3 manage.py migrate --settings=config.settings.development
** +python3 manage.py migrate --settings=config.settings.development
**
## Run for Dev -**python3 manage.py runserver --settings=config.settings.development** +**python3 manage.py runserver --settings=config.settings.development**


+## Run Periodic Tasks with RabbitMQ +### Installition RabbitMQ +**sudo apt-get install rabbitmq-server**
+**sudo systemctl start rabbitmq-server**
+### Run Celery +**celery -A config worker -l info**
+**celery -A config beat -l info**

+## Run Periodic Tasks with Redis +### **add these two settings to base settings**
+**CELERY_BROKER_URL = "redis://localhost:6379"
+CELERY_RESULT_BACKEND = "redis://localhost:6379"** +### Installition Redis +**sudo apt install redis**
+**redis-server**
+### Run Celery +**celery -A config worker -l info**
+**celery -A config beat -l info** \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 2e4621a..87c6cdb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,32 @@ +amqp==5.1.1 asgiref==3.5.2 +async-timeout==4.0.2 +billiard==3.6.4.0 +celery==5.2.7 certifi==2022.6.15 charset-normalizer==2.1.1 +click==8.1.3 +click-didyoumean==0.3.0 +click-plugins==1.1.1 +click-repl==0.2.0 +Deprecated==1.2.13 Django==4.1 django-autoslug==1.9.8 +django-compat==1.0.15 django-crispy-forms==1.14.0 django-environ==0.9.0 +django-smtp-ssl idna==3.3 +kombu==5.2.4 +packaging==21.3 +prompt-toolkit==3.0.31 +pyparsing==3.0.9 +pytz==2022.2.1 +redis==4.3.4 requests==2.28.1 +six==1.16.0 sqlparse==0.4.2 urllib3==1.26.12 -django-smtp-ssl \ No newline at end of file +vine==5.0.0 +wcwidth==0.2.5 +wrapt==1.14.1 diff --git a/src/config/__init__.py b/src/config/__init__.py index e69de29..e31568a 100644 --- a/src/config/__init__.py +++ b/src/config/__init__.py @@ -0,0 +1,3 @@ +from .celery import app as celery_app + +__all__ = ("celery_app",) \ No newline at end of file diff --git a/src/config/celery.py b/src/config/celery.py new file mode 100644 index 0000000..16f7b93 --- /dev/null +++ b/src/config/celery.py @@ -0,0 +1,15 @@ +import os +from celery import Celery + + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.development") +app = Celery("django_celery") +app.config_from_object("django.conf:settings", namespace="CELERY") +app.autodiscover_tasks() + +app.conf.beat_schedule = { + 'send-emails': { + 'task': 'send_emails', + 'schedule': 86400, + }, +} \ No newline at end of file diff --git a/src/config/settings/base.py b/src/config/settings/base.py index 3e9c0b0..35f672f 100644 --- a/src/config/settings/base.py +++ b/src/config/settings/base.py @@ -92,4 +92,7 @@ EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD") EMAIL_HOST_USER = 'series.notification@gmail.com' EMAIL_PORT = 465 EMAIL_USE_SSL = True -DEFAULT_FROM_EMAIL = EMAIL_HOST_USER \ No newline at end of file +DEFAULT_FROM_EMAIL = EMAIL_HOST_USER + +# CELERY_BROKER_URL = "redis://localhost:6379" +# CELERY_RESULT_BACKEND = "redis://localhost:6379" \ No newline at end of file diff --git a/src/series/tasks.py b/src/series/tasks.py new file mode 100644 index 0000000..1de5839 --- /dev/null +++ b/src/series/tasks.py @@ -0,0 +1,79 @@ +from celery import shared_task +from account.models import User +from requests import get +from datetime import datetime +from django.template.loader import render_to_string +from django.core.mail import EmailMessage +from celery.utils.log import get_task_logger + + +logger = get_task_logger(__name__) + +def get_request(link): + raw_data = get(link) + + if raw_data.status_code == 200: + data = raw_data.json() + if not data['errorMessage']: + return data + logger.info(f"error message: {data['errorMessage']}\nlink: {link}") + return None + logger.info(f"status code: {raw_data.status_code}\nlink: {link}") + +def episode_counter(imdb_api_key, s, data): + now_date = datetime.strptime(datetime.strftime(datetime.utcnow(),'%d %b %Y'), '%d %b %Y') + new_episodes_count = 0 + + for n in data['tvSeriesInfo']['seasons'][data['tvSeriesInfo']['seasons'].index(str(s.last_season)):]: + data = get_request(f"https://imdb-api.com/en/API/SeasonEpisodes/{imdb_api_key}/{s.imdb_id}/{n}") + if data is None: return None + + episodes = data['episodes'] + for i in range(int(s.last_episode) if n == str(s.last_season) else 0, len(episodes)): + released_date = episodes[i]['released'].replace('.', '') + try: + episode_date = datetime.strptime(released_date, '%d %b %Y') + if (episode_date - now_date).days > 0: + raise ValueError + except ValueError: + return new_episodes_count, int(last_n)+1, last_i+1 if new_episodes_count > 0 else 0, 0, 0 + last_i = i + new_episodes_count += 1 + last_n = n + + return new_episodes_count, n, i+1 if new_episodes_count > 0 else 0, 0, 0 + +@shared_task(name='send_emails') +def send_feedback_email_task(): + users = User.objects.filter(email_notification_is_active=True) + for user in users: + series = user.series.filter(show=True).order_by('-id') + series_new_episodes = [] + + for s in series: + data = get_request(f"https://imdb-api.com/en/API/Title/{user.imdb_api_key}/{s.imdb_id}") + + if data is None: + series_new_episodes = [] + break + data = episode_counter(user.imdb_api_key, s, data) + if data is None: + series_new_episodes = [] + break + + if data[0]: + series_new_episodes.append([s, + {'count': data[0], + 'last_season': data[1], + 'last_episode': data[2]} + ]) + + if series_new_episodes: + message = render_to_string('new_episodes_verification.html', { + 'user': user, + 'data': series_new_episodes + }) + email = EmailMessage( + 'New Episodes!', message, to=[user.email] + ) + email.send() \ No newline at end of file diff --git a/src/series/templates/new_episodes_verification.html b/src/series/templates/new_episodes_verification.html new file mode 100644 index 0000000..d2ea670 --- /dev/null +++ b/src/series/templates/new_episodes_verification.html @@ -0,0 +1,9 @@ +{% autoescape off %} + +Hi {{ user.username }}, +There are new episodes of the series you recorded. +{% for s, d in data %} +{{d.count}} episodes of {{s.title}} you haven't watched (watched episode: {{s.last_season}} - {{s.last_episode}}). +{% endfor %} + +{% endautoescape %} \ No newline at end of file