Skip to content

Commit

Permalink
Merge pull request #244 from ZUS666/develop
Browse files Browse the repository at this point in the history
up
  • Loading branch information
ZUS666 authored Oct 29, 2023
2 parents 3f2b603 + e6b356f commit 7975c4e
Show file tree
Hide file tree
Showing 17 changed files with 189 additions and 57 deletions.
17 changes: 14 additions & 3 deletions api_spot/api/views/avatar.py

Large diffs are not rendered by default.

6 changes: 1 addition & 5 deletions api_spot/api/views/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from api.serializers.order import (
OrderGetSerializer, OrderSerializer, OrderUpdateSerializer,
)
from api.services.orders import order_cancel_email, order_confirmation_email
from api.services.orders import order_cancel_email
from spots.constants import CANCEL, PAID, WAIT_PAY
from spots.models import Order

Expand All @@ -34,10 +34,6 @@ def get_serializer_class(self):
return OrderUpdateSerializer
return super().get_serializer_class()

def perform_create(self, serializer):
instance = serializer.save()
order_confirmation_email(instance)

def update(self, request, location_id, spot_id, pk, *args, **kwargs):
instance = get_object_or_404(
Order, pk=pk,
Expand Down
4 changes: 2 additions & 2 deletions api_spot/api/views/pay.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from api.exceptions import OrderStatusError
from api.permissions import IsOwnerOrReadOnly
# from api.serializers.pay import PaySerializer
from api.services.orders import order_finished_email
from api.services.orders import order_confirmation_email
from spots.constants import PAID, WAIT_PAY
from spots.models import Order

Expand Down Expand Up @@ -47,7 +47,7 @@ def patch(
raise OrderStatusError
order.status = PAID
order.save()
order_finished_email(order)
order_confirmation_email(order)
return Response(
# serializer.data,
{'message': 'Заказ оплачен'},
Expand Down
4 changes: 0 additions & 4 deletions api_spot/api_spot/celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,4 @@
'task': 'spots.tasks.repeat_orders_finish',
'schedule': crontab(hour='*/1', minute=1),
},
'every_day_check_email': {
'task': 'promo.tasks.every_day_check_today_email_task',
'schedule': crontab(hour=15, minute=30),
},
}
7 changes: 6 additions & 1 deletion api_spot/api_spot/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
'drf_spectacular',
'gmailapi_backend',
'django_celery_beat',
'django_celery_results',
'corsheaders',
'ckeditor',

Expand Down Expand Up @@ -170,9 +171,11 @@
GMAIL_API_REFRESH_TOKEN = os.getenv('GMAIL_API_REFRESH_TOKEN')

CELERY_BROKER_URL = os.getenv('CELERY_BROKER')
CELERY_RESULT_BACKEND = os.getenv('CELERY_BROKER')
CELERY_RESULT_BACKEND = 'django-db'
CELERY_TIMEZONE = 'Europe/Moscow'
CELERY_TASK_TRACK_STARTED = True
CELERY_CACHE_BACKEND = 'default'
CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler'

CACHES = {
'default': {
Expand Down Expand Up @@ -204,6 +207,8 @@
'region_name': AWS_S3_REGION_NAME,
'use_ssl': AWS_S3_USE_SSL,
'endpoint_url': AWS_S3_ENDPOINT_URL,
'file_overwrite': False,
'querystring_expire': 24 * 60 * 60,
}
},
'staticfiles': {
Expand Down
11 changes: 10 additions & 1 deletion api_spot/promo/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@admin.register(Promocode)
class PromcodeAdmin(admin.ModelAdmin):
list_display = (
'id',
'name',
'percent_discount',
'expiry_date',
Expand All @@ -16,10 +17,18 @@ class PromcodeAdmin(admin.ModelAdmin):
@admin.register(EmailNews)
class EmailNewsAdmin(admin.ModelAdmin):
list_display = (
'id',
'subject_message',
'send_date',
'send_datetime',
'promocode',
'is_sent',
'text_message',
)
exclude = ('is_sent',)
readonly_fields = ('task',)

def delete_queryset(self, request, queryset):
for obj in queryset:
if obj.task:
obj.task.delete()
return super().delete_queryset(request, queryset)
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 4.2.5 on 2023-10-28 01:14

from django.db import migrations, models
import django.db.models.deletion
import promo.validators


class Migration(migrations.Migration):

dependencies = [
('django_celery_beat', '0018_improve_crontab_helptext'),
('promo', '0001_initial'),
]

operations = [
migrations.AlterModelOptions(
name='emailnews',
options={'ordering': ('send_datetime',), 'verbose_name': 'Email', 'verbose_name_plural': 'Emails'},
),
migrations.RemoveField(
model_name='emailnews',
name='send_date',
),
migrations.AddField(
model_name='emailnews',
name='send_datetime',
field=models.DateTimeField(blank=True, null=True, unique=True, validators=[promo.validators.validate_datetime_less_present], verbose_name='Дата и время отправки'),
),
migrations.AddField(
model_name='emailnews',
name='task',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='django_celery_beat.periodictask'),
),
]
21 changes: 21 additions & 0 deletions api_spot/promo/migrations/0003_alter_emailnews_send_datetime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 4.2.5 on 2023-10-28 01:15

from django.db import migrations, models
import django.utils.timezone
import promo.validators


class Migration(migrations.Migration):

dependencies = [
('promo', '0002_alter_emailnews_options_remove_emailnews_send_date_and_more'),
]

operations = [
migrations.AlterField(
model_name='emailnews',
name='send_datetime',
field=models.DateTimeField(default=django.utils.timezone.now, unique=True, validators=[promo.validators.validate_datetime_less_present], verbose_name='Дата и время отправки'),
preserve_default=False,
),
]
30 changes: 26 additions & 4 deletions api_spot/promo/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.db import models
from django.db import models, transaction
from django_celery_beat.models import PeriodicTask

from promo.constants import MAX_PROMO_DISCOUNT
from promo.validators import (
Expand Down Expand Up @@ -41,8 +42,8 @@ class EmailNews(models.Model):
text_message = models.TextField(
'Текст письма'
)
send_date = models.DateField(
'Дата отправки',
send_datetime = models.DateTimeField(
'Дата и время отправки',
validators=(validate_datetime_less_present,),
unique=True,
)
Expand All @@ -57,11 +58,32 @@ class EmailNews(models.Model):
'Отправлен',
default=False,
)
task = models.OneToOneField(
PeriodicTask,
on_delete=models.CASCADE,
blank=True,
null=True,
)

def __str__(self):
return self.subject_message[:20]

class Meta:
verbose_name = 'Email'
verbose_name_plural = 'Emails'
ordering = ('send_date',)
ordering = ('send_datetime',)

@transaction.atomic
def save(self, *args, **kwargs):
from promo.services import create_task_after_save_promo_email
if self.task:
self.task.delete()
self.is_sent = False
self.task = create_task_after_save_promo_email(self)
super().save(*args, **kwargs)

@transaction.atomic
def delete(self, *args, **kwargs):
if self.task:
self.task.delete()
super().delete(*args, **kwargs)
35 changes: 31 additions & 4 deletions api_spot/promo/services.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import json

from django.contrib.auth import get_user_model
from django_celery_beat.models import ClockedSchedule, PeriodicTask

from promo.models import Promocode
from promo.constants import NEWS_EMAIL_TEMPLATE, PROMOCE_EMAIL_TEMPLATE


User = get_user_model()
Expand All @@ -24,14 +27,38 @@ def get_data_news(text_message):
return context


def get_data_promo(promocode_id):
def get_data_promo(promocode):
"""
Создание контекста для письма с промокодом.
"""
promocode = Promocode.objects.get(id=promocode_id)
context = {
'promocode_name': promocode.name,
'percent_discount': promocode.percent_discount,
'expiry_date': promocode.expiry_date
'expiry_date': promocode.expiry_date.isoformat(),
}
return context


def create_task_after_save_promo_email(obj):
"""
Принимает объект письма
"""
context = get_data_news(obj.text_message)
template = NEWS_EMAIL_TEMPLATE
if obj.promocode:
context = {**context, **get_data_promo(obj.promocode)}
template = PROMOCE_EMAIL_TEMPLATE
time = ClockedSchedule.objects.create(clocked_time=obj.send_datetime)
task = PeriodicTask.objects.create(
name=f'Email subject:{obj.subject_message} '
f'date:{obj.send_datetime.date().isoformat()}',
task='promo.tasks.create_chunk_task_send_mail',
clocked=time,
one_off=True,
args=json.dumps([
obj.subject_message,
template,
context,
])
)
return task
39 changes: 14 additions & 25 deletions api_spot/promo/tasks.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
from django.core.mail import EmailMultiAlternatives
from django.forms.models import model_to_dict
from django.template.loader import render_to_string
from django.utils import timezone

from api_spot.celery import app
from promo.constants import NEWS_EMAIL_TEMPLATE, PROMOCE_EMAIL_TEMPLATE
from promo.models import EmailNews
from promo.services import get_data_news, get_data_promo, get_list_emails
from promo.services import get_list_emails


@app.task
Expand All @@ -25,29 +22,21 @@ def send_mail_news(user_email, subject, template, context):


@app.task
def create_chunk_task_send_mail(email):
subject_message = email.get('subject_message')
text_message = email.get('text_message')
promocode_id = email.get('promocode')
context = get_data_news(text_message)
template = NEWS_EMAIL_TEMPLATE
if promocode_id:
context = {**context, **get_data_promo(promocode_id)}
template = PROMOCE_EMAIL_TEMPLATE
def create_chunk_task_send_mail(subject_message, template, context):
"""
Создает chunk для отправки писем.
"""
emails = get_list_emails()
chunk = [(email, subject_message, template, context) for email in emails]
send_mail_news.chunks((chunk), 5).apply_async()
task = send_mail_news.chunks((chunk), 5).apply_async()
change_email_status.delay(subject_message)
return task.ready()


@app.task
def every_day_check_today_email_task():
email = EmailNews.objects.filter(
send_date=timezone.now()
).exclude(is_sent=True).first()
dict_email = model_to_dict(email)
if email:
create_chunk_task_send_mail(dict_email)
email.is_sent = True
email.save()
return f'{email.subject_message} sent'
return 'There are no emails to send today'
def change_email_status(subject_message):
"""
Меняет статус письма.
"""
EmailNews.objects.filter(
subject_message=subject_message).update(is_sent=True)
5 changes: 2 additions & 3 deletions api_spot/promo/validators.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import datetime as dt

from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator
from django.utils import timezone

from promo.constants import MAX_PROMO_DISCOUNT_MESSAGE


def validate_datetime_less_present(value):
if value < dt.date.today():
if value < timezone.now():
raise ValidationError('Значение должно быть в будущем')


Expand Down
3 changes: 2 additions & 1 deletion api_spot/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ gunicorn==21.2.0
phonenumbers==8.13.20
Pillow==10.0.1
psycopg2-binary==2.9.7
python-dotenv==1.0.0
python-dotenv==1.0.0
django-celery-results==2.5.1
17 changes: 17 additions & 0 deletions api_spot/spots/migrations/0014_alter_order_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.5 on 2023-10-27 14:16

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('spots', '0013_alter_location_days_open'),
]

operations = [
migrations.AlterModelOptions(
name='order',
options={'ordering': ('-date', 'start_time'), 'verbose_name': 'Заказ', 'verbose_name_plural': 'Заказы'},
),
]
2 changes: 1 addition & 1 deletion api_spot/spots/models/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class Meta:
"""Класс Meta для Order описание метаданных."""
verbose_name = 'Заказ'
verbose_name_plural = 'Заказы'
ordering = ('date', 'start_time')
ordering = ('-date', 'start_time')

def __str__(self) -> str:
return f'Локация id = {self.spot.location.id}, спот={self.spot.id}'
9 changes: 7 additions & 2 deletions api_spot/spots/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from celery import shared_task
from django.shortcuts import get_object_or_404

from api.services.orders import order_finished_email
from api_spot.celery import app
from spots.constants import FINISH, NOT_PAID, PAID, WAIT_PAY

Expand All @@ -24,9 +25,13 @@ def change_status_task(order_id: int) -> str:
def repeat_orders_finish() -> str:
"""Периодичная задача, которая завершает заказы, которые закончились."""
hour = int(datetime.datetime.now().time().isoformat('hours'))
Order.objects.filter(
orders = Order.objects.filter(
date__lte=datetime.datetime.now().date(),
end_time=datetime.time(hour),
status=PAID
).update(status=FINISH)
)
for order in orders:
order.status = FINISH
order.save()
order_finished_email(order)
return "Статусы заказов, которые закончились изменены"
Loading

0 comments on commit 7975c4e

Please sign in to comment.