From fae45abcee5ee47d5a1a02b3afef359c0d32372c Mon Sep 17 00:00:00 2001 From: NikDizell Date: Fri, 14 Nov 2025 13:20:44 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D0=BE=D0=BF=D0=BE=D0=B2=D0=B5=D1=89=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D0=BE=20=D0=BD=D0=BE=D0=B2=D0=BE=D0=B9=20=D0=B7=D0=B0=D1=8F?= =?UTF-8?q?=D0=B2=D0=BA=D0=B5=20=D0=BD=D0=B0=20=D0=BF=D0=BE=D1=87=D1=82?= =?UTF-8?q?=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__pycache__/settings.cpython-310.pyc | Bin 2811 -> 3192 bytes OneCprogsite/OneCprogsite/settings.py | 22 ++++++ .../__pycache__/admin.cpython-310.pyc | Bin 5134 -> 5647 bytes .../__pycache__/models.cpython-310.pyc | Bin 6069 -> 6537 bytes OneCprogsite/programmer/admin.py | 12 ++++ .../__pycache__/test_email.cpython-310.pyc | Bin 0 -> 1170 bytes .../management/commands/send_daily_summary.py | 33 +++++++++ .../management/commands/test_email.py | 29 ++++++++ .../0013_callbackrequest_notification_sent.py | 18 +++++ ...krequest_notification_sent.cpython-310.pyc | Bin 0 -> 720 bytes OneCprogsite/programmer/models.py | 14 ++++ .../emails/callback_notification.html | 57 +++++++++++++++ .../templates/emails/daily_summary.html | 60 ++++++++++++++++ .../email_notifications.cpython-310.pyc | Bin 0 -> 1977 bytes .../programmer/utils/email_notifications.py | 65 ++++++++++++++++++ 15 files changed, 310 insertions(+) create mode 100644 OneCprogsite/programmer/management/commands/__pycache__/test_email.cpython-310.pyc create mode 100644 OneCprogsite/programmer/management/commands/send_daily_summary.py create mode 100644 OneCprogsite/programmer/management/commands/test_email.py create mode 100644 OneCprogsite/programmer/migrations/0013_callbackrequest_notification_sent.py create mode 100644 OneCprogsite/programmer/migrations/__pycache__/0013_callbackrequest_notification_sent.cpython-310.pyc create mode 100644 OneCprogsite/programmer/templates/emails/callback_notification.html create mode 100644 OneCprogsite/programmer/templates/emails/daily_summary.html create mode 100644 OneCprogsite/programmer/utils/__pycache__/email_notifications.cpython-310.pyc create mode 100644 OneCprogsite/programmer/utils/email_notifications.py diff --git a/OneCprogsite/OneCprogsite/__pycache__/settings.cpython-310.pyc b/OneCprogsite/OneCprogsite/__pycache__/settings.cpython-310.pyc index b93c66dffb1acadc192f64b240db8e0e159ab8db..f260f30f522b7ac90d70c72398b0aaff24047b6b 100644 GIT binary patch delta 452 zcmew@`a^;*pO=@50SG2Cif6V8Pvnzfl4G2xbK6}xN2sN-33TfpUsk ziuyvPC>1bUBSjO)R!vojQUhXju$Weg_Clrw8Ywyp8KX2)n1UHJbvN@c?q#p9(oV@r z%uCPLOU^G!)yqxH%+X6qOwLZtODWbX&Mhg>a|MYxL4>OKz(VO@Wy$%unchqc47d1Q zeH}f0;ywI>L*he&U4yEG^D?tjGOJQ^avb1FZwVu*3UCY#4)+gosS-9TEXyrds7y{y zQphbXEK|tMEy%jXy5M0HVS>ljaoLZDpS(>d^RQiiiM^kI_X^vFJ`dhpZ zGo2iry`gmY!OYnpajN1Pt3EVIXl!$i>yoG1Mm{ z-Yv-A7h>-%9-vRcT!WxI*5ZPk%#tDzpz4(jMOGl4R+BBc5*Q06&*F*~VPRxpX5j>q Lj66UHqQzJM5mSgk delta 70 zcmew%@mrKHpO=@50SG#0i)6O*Pvnzfy2CJ0=Qb--FoUM@W`a>Sn-6fNGIFkD YDAEI}EYh32ggb#TZZa!RJQD{a0GrGa!vFvP diff --git a/OneCprogsite/OneCprogsite/settings.py b/OneCprogsite/OneCprogsite/settings.py index 65ac19e..2d73e10 100644 --- a/OneCprogsite/OneCprogsite/settings.py +++ b/OneCprogsite/OneCprogsite/settings.py @@ -137,3 +137,25 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_URL = '/media/' + +# Настройки email +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +# EMAIL_HOST = 'smtp.yandex.ru' # или smtp.gmail.com, smtp.mail.ru +# EMAIL_PORT = 587 +# EMAIL_USE_TLS = True +# EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER', 'it@yandex.ru') +# EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', 'tifdctkrcjcqwxyc') +# DEFAULT_FROM_EMAIL = EMAIL_HOST_USER +# SERVER_EMAIL = EMAIL_HOST_USER + +EMAIL_HOST = 'smtp.gmail.com' # или smtp.gmail.com, smtp.mail.ru +EMAIL_PORT = 587 +EMAIL_USE_TLS = True +EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER', 'nikdizell@gmail.com') +EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', 'qvmw yccb msqv mmpj') +DEFAULT_FROM_EMAIL = EMAIL_HOST_USER +SERVER_EMAIL = EMAIL_HOST_USER + +# Email для уведомлений (можно указать несколько через запятую) +# ADMIN_EMAILS = os.getenv('ADMIN_EMAILS', 'nikdizell@gmail.com').split(',') +ADMIN_EMAILS = os.getenv('ADMIN_EMAILS', 'it@nserdyuk.ru').split(',') diff --git a/OneCprogsite/programmer/__pycache__/admin.cpython-310.pyc b/OneCprogsite/programmer/__pycache__/admin.cpython-310.pyc index e52d92c8596be2940ec0928d6722fd2ab3cd9e0e..38271a28f843776cac1db81b3bdca00f1d3bd60a 100644 GIT binary patch delta 1380 zcmZ`&?Qh#e8234LVmprW)|RzfSvI6XXz7;@%2+{6S*I4pfOUX~HloybZM`Njy^A#! zB}%nbssz)DbmCK42kZmz0ckKQHd@+$!TxF?jgR~jNId5-Myuh|{rtS%{hsIUZa%j@ zV!5f5A;EX_uRdq<@Q>DniZ=P1>TqXX!rC{etYaM;t5Vs(Nn%uz*u*K&Ol%Rel3bJK z5KiOFs#Z?nEY5+)!g*prTo3LAEsgs~8nmbIA<#0opJe{Y8o-AkD~pfdLGa|(Bt?<*wv=vHtZO99(@TA;xJ1v z*;xRLuw0Yss2xKRjl(S_($vQd?OvTGCf?3t^?DY)H7H z-im!KLz)D{;^(Oj%N0>L5HYX?%c{M&j|xQ);*A0Lcl@9HE&peK*WdBCLHft_nJxAv8NKe!g$C`1Jn zg6mIelPyE8B=Otv)uZ$jNMX#Ky>gJ^32>agS4ybJps#^UUlBQ%wIN-k&miQ@S`kA*t zrEd#3&@0iV4~6|*{;fgLIX;lQ@c+@GFO_cedNM#$ylM`Q&48g8>(;7xw8qLw+ha|) zUJ(j?L&QhBMu&3|)H%Lq{)s~VU1~I{i(cTrr#5=eh{#C+QA@-NpzrY;R{y}iO`(^- zSk|r*vRJ9kyYwTzXB|-mgF64innXQ(C|w#C7a+{C1(!A}m@u04B$7 z1t~hiH`0SIM06vjqhK6{$wmM)Ns$$}L){_nlP1S?{xE$Bt#Ci{EVnbq(OM+^0Mbp6 zrqLIG{d_FzC9jAL907~`UUu@tln@1zP;)6Jv@*L~l>c*@2bv}#@9>e_2PY%;^CJ84 zK|ZFu0FEzVG7~_PG`V~6dt|!6cXAK99Ts(k+B@*>-gn4V@O+wI%3sVS#LyBw$kHuj Hp&a@fNQ-fE delta 926 zcmZ8fNoy2A6z-bz%v5@tJuxCKIm8Uns5ntW#)U*pqGZv4!6DerbX8(>k~Y=d#OOhh zK$L(A@8Cfc!IKxkE#N`OU(jQpJbDmsf=6F<3<=XkeZ1v+)$e^(PY*olv|Q8F6#V}D zZJ8~%y|>D7@(3=*yK0QG=4GX#F^y}szM@PMrZZ!iRt#n`3o|B5ansgWnq|f`5t6E2V03&5s)0Rjd z9M;E9oI(GsDkM>yMF=T!#gfPUFd5utRiCrQKldPQ5I*REHi%YH9X&7l;D|At7?Ouv zfColvVnmuF@X`=u5Yp!5&W)UmHq3DxV-pb@;qD$-*IVGVxkmb7(&{=ohQVS})Ly6t zqV836C-8iC&X$fiCkvwkBTdH9t-xpNC#k@zRCl6Jrbpp>>c#!CG*3!6E#Z`eO9-LC ztIiGEap!%0#}^3<6%$)>h)E1o=pyI0?COjwuF8_+Cc9x2*=!ZJw;RDAX+oRQh;qTN z^dXXiU6}z=g6o;qYw{k1R8kz1AXN~ueIXACP1|8pvB#Q=)X6;5^7jCZu zfhuZDC7X*9_>q|+K77mW2bOIo_af^ZS`X10CXW%@pp*-Aj>UpWn1)ZebEST1%HBfi z3dV(9TPnu3$%Q5@oAd2NTb5dj5h3IzV gL?Io=2Yh9gFJfl3u#g+dCgq~ZJh7TBVv#KQ2Zyc90RR91 diff --git a/OneCprogsite/programmer/__pycache__/models.cpython-310.pyc b/OneCprogsite/programmer/__pycache__/models.cpython-310.pyc index e776b3897c68ef9cb08f6741cd726e45e15fb351..d7080693d112c6a392c1a2ff36ffa704667ba022 100644 GIT binary patch literal 6537 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 diff --git a/OneCprogsite/programmer/admin.py b/OneCprogsite/programmer/admin.py index 18a9935..5962719 100644 --- a/OneCprogsite/programmer/admin.py +++ b/OneCprogsite/programmer/admin.py @@ -49,6 +49,18 @@ class CallbackAdmin(admin.ModelAdmin): search_fields = ('name', 'phone', 'email') readonly_fields = ('time_create',) actions = ['mark_as_read', 'mark_as_unread', 'mark_as_processed'] + actions = ['mark_as_read', 'mark_as_unread', 'mark_as_processed', 'resend_notification'] + + def resend_notification(self, request, queryset): + from .utils.email_notifications import send_callback_notification + count = 0 + for callback in queryset: + success = send_callback_notification(callback) + if success: + count += 1 + self.message_user(request, f'Уведомления отправлены для {count} заявок') + + resend_notification.short_description = "Переотправить email уведомления" def new_badge(self, obj): if not obj.is_read: diff --git a/OneCprogsite/programmer/management/commands/__pycache__/test_email.cpython-310.pyc b/OneCprogsite/programmer/management/commands/__pycache__/test_email.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20b96873f8dee49485d5b25f7e25af1794955384 GIT binary patch literal 1170 zcmZuwOK;Oa5Z?8hG;M_lr~(PO5|>PoC6s;8E^8f%H}=@`94L?ik+B09xqs%H?xMNRWJoJLJ7R-Olpwz4D% zQUa#MDCbc+V4$xvni8K=#(kOuQ5?!6%R>QU2PWG9L6L_E@)UwSl_*3#N1jGBxJnmh z<$t}mX)q>Wu$&X7s?W* z0;b)-G{E#5m=2gR$GCZhKB}i`{%qzMq;+|V7+2+O1i96XC0--u0&iOF=|f2fp6h>{ z9LJf~_YZduUi+^)?(vpAw~jhpY299|`6upv7ofG>y~BgHUoN;?cCiWMe}4aDFFhON zgxjnLL&{jch~vp-@kH*r_-wy`98qHP%$A9>W#VRWId*GE!*}*1EAlGRd77Q(iZB=_ zS;2+=A&)rCRd6S2i{R$E3|p)(@G@voUk*8!sS`Tmlb8zAJ!!S}U03M)-EODb)P%-p z+!tDq5125su}nND6`3C>zXPZLyxH689ZmQ!ORJxMlxHIv@*Wul=^%q2gD`uYl(xos zHYg3LFg7d78cGE&4+ulxS{B8aOMZAjyBeiwX~oR&mGVzRhR zn9KlSAr04+5^Fb2@nFW%3A3C!3og!EXwzIaoLKp{Bkh;T@*XYdI|Ywo=9Dewrx}m> jQCM~uD|1s`vlNbe@m$6vtfw4NhZ1;??H&L(q z6rQ|^xFD$U6?(6I_2j`95Ol4ctYmQus=B_aqO0obRvQ~VM)LH}Ap6M~`|iPM2?=bG znyX}h0nb^%V_pPtz}Z6v0f;vY1oF9fEn)%T6EM8rLoqU~W>M zIxQUnTcl=023gF3#R2dcj|GPFmT!RVc?UvhLHi(p&Vis@1Q9jry6&TY9elfmR@AR3 zP`k^Uo7Zp1G|BTxl0MhCSEH$9W~9ah+`0b}?n0@mY?`Ibu+m^zZP(XmAp6#Su!r`e zUD_}9GkIU_Cwq9sE{|T>Z$~ffdrGguOB$d9I8Exja{U*mC(58KlLCi=i15od$b~-U zTGI@3*r637*~F!EJt?WK&XN4SlzVlO`&y3x7gz$8X@*7zG?&0kW0)u9OwnPo3dfI2yzj|uz?#+Z z`aQ?Q@69v(o?&sc*w_9GwMorIG7$?zcpSC@9`HU7e@DknerFiE?rMe+Oqvawy_)2D Yft5F)dt7vT#nm19waoY)=YxUx3$IGyM*si- literal 0 HcmV?d00001 diff --git a/OneCprogsite/programmer/models.py b/OneCprogsite/programmer/models.py index 4b08fa7..4f3165e 100644 --- a/OneCprogsite/programmer/models.py +++ b/OneCprogsite/programmer/models.py @@ -4,6 +4,7 @@ from django.utils import timezone from django.db.models.signals import post_save, post_delete from django.dispatch import receiver from django.core.cache import cache +from .utils.email_notifications import send_callback_notification class Recall(models.Model): @@ -95,6 +96,7 @@ class CallbackRequest(models.Model): time_create = models.DateTimeField(auto_now_add=True, verbose_name='Дата создания') is_processed = models.BooleanField(default=False, verbose_name='Обработано') is_read = models.BooleanField(default=False, verbose_name='Прочитано') + notification_sent = models.BooleanField(default=False, verbose_name='Уведомление отправлено') def __str__(self): return f"{self.name} - {self.phone}" @@ -105,6 +107,18 @@ class CallbackRequest(models.Model): ordering = ['-time_create'] +# Сигнал для отправки уведомления при создании заявки +@receiver(post_save, sender=CallbackRequest) +def send_callback_email_notification(sender, instance, created, **kwargs): + if created and not instance.notification_sent: + # Отправляем email уведомление + success = send_callback_notification(instance) + if success: + instance.notification_sent = True + # Сохраняем без повторного вызова сигнала + sender.objects.filter(pk=instance.pk).update(notification_sent=True) + + class PageView(models.Model): url = models.CharField(max_length=500) timestamp = models.DateTimeField(default=timezone.now) diff --git a/OneCprogsite/programmer/templates/emails/callback_notification.html b/OneCprogsite/programmer/templates/emails/callback_notification.html new file mode 100644 index 0000000..4e7cdd8 --- /dev/null +++ b/OneCprogsite/programmer/templates/emails/callback_notification.html @@ -0,0 +1,57 @@ + + + + + + + + +
+
+

🚨 Новая заявка на сайте

+

Требуется ваше внимание!

+
+ +
+
+ ⚠️ Срочно! Пользователь оставил заявку на обратный звонок. +
+ +
+

📋 Информация о заявке:

+

👤 Имя: {{ callback.name }}

+

📞 Телефон: {{ callback.phone }}

+ {% if callback.email %} +

📧 Email: {{ callback.email }}

+ {% endif %} + {% if callback.question %} +

❓ Вопрос:
{{ callback.question }}

+ {% endif %} +

🕒 Время отправки: {{ callback.time_create|date:"d.m.Y H:i" }}

+
+ + + +

Не забудьте отметить заявку как обработанную после связи с клиентом!

+
+ + +
+ + \ No newline at end of file diff --git a/OneCprogsite/programmer/templates/emails/daily_summary.html b/OneCprogsite/programmer/templates/emails/daily_summary.html new file mode 100644 index 0000000..7886975 --- /dev/null +++ b/OneCprogsite/programmer/templates/emails/daily_summary.html @@ -0,0 +1,60 @@ + + + + + + + + +
+
+

📊 Ежедневная сводка

+

Статистика заявок за {{ yesterday|date:"d.m.Y" }}

+
+ +
+
+
+

📅 Вчерашние заявки

+

{{ yesterday_callbacks }}

+
+ +
+

⏳ Ожидают обработки

+

{{ unprocessed_callbacks }}

+
+
+ + {% if unprocessed_callbacks > 0 %} +
+ ⚠️ Внимание! У вас есть {{ unprocessed_callbacks }} необработанных заявок. +
+ {% endif %} + + + +

Не забудьте обработать все pending заявки!

+
+ + +
+ + \ No newline at end of file diff --git a/OneCprogsite/programmer/utils/__pycache__/email_notifications.cpython-310.pyc b/OneCprogsite/programmer/utils/__pycache__/email_notifications.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..12f89e7a7cd93b53e317698bd2d260c16b566020 GIT binary patch literal 1977 zcmah~&2Jk;6rWkI*K5aiOe(@REO0;uDM;m3Aw*~#Q7LJv+Es-KT5UaJXTyG&*;$G$ zrz$Ptf+`N&fYMe%0&b;hL`a+PFRZV7<%GEOT%^K#v$hE*)}A+CZ{K?}@ArEfRw^X| z&!2xUy5%AvzvJZSlY`0I(4sa7Mi}*oPutYj+8V{V=H-07t*dd)%lk&#P-ERI_-5Nw zNKqhP~yUtp&d!#20d6aBQ7NjaleGCeOo%c=yZpo<;jzW9uIsHT67=8 z@Et}+WSe|Lu4_9~Ql^cF)V6bC0VT^lMFPrnmj70}s_ke}A8A+VPEO`w*ML^&!903R znm3EgxJ4;BPgtQzWNAc4IoO|rRvu}rz|3oKF`!prMetWee-r&uljuaw4?dt|2QG3r zudL0D^nZ2UBdolmU((TbHo8bJl6obM@H`eg$#>%`$#3J!$?fD$a&P=)vOB(FjjzIF zH@TY}CijzjFgi%~l3gn~v|w}ydxy!dR`N@78}#oa`?z(G+=l(+XS4(s55`|)DyWO^ z>??0^1N=U?@%4{Zavc`$poOD`z{dX?0yhxi@I{VEj$ZFLo_E3Nd}s%ubh~Z`avcUs zThjLgCNM2}A*gJHkpz&KQ*Fq)E4dvDZ}_T0i#0`o^^C)t9JS}xa=-Vosv?_SV?YlgXw&zANEn^_|RI}2BknPCzVBZ@I z-^e^44IIJ3{k;Mw6%Sf}f?R1X}V z3mvXNAb8Wt^4i+Q`NoRF_@0 zrk-!trsuH*|0w1_I^>q9f@oLzp5q3yE2I1;BA$bdhwrbP*gP|kTVXKi>@y<7Ol-1k zC+LOHVNN$U0)7$_)>9K42*-z;*o-B>Wm8?(f3OlQ^#|$Gs(Q@AI(nUR;5<4GLetAs z&uMglnvYDQmMc+%YB1NR4nORThYLl6R<#K5%PR4)&A13|<*`p8=Z>ZhZBF)q;#! z8Fmr+){46S;qlg%8(HvoEQuN!GL^<4jKxIFa{7I5O4HSPK1)4bvM8gdpqK~I8Xi-Q zCT4&+oFTLe3VaIG;t5oK5(Ne)9!IeVqE5xLI7Z-P;&~XQ^D4_2Tt-*SYJ`fKj_I2w z()i0LROu{<7AVx`(^>|zrA{dL64st94*bxii<3RceSojzOJ3-}cSg}KotwQFbTckK zHd#@S%xab5V*yD9q)v-HF4r=`nYN8gw(ab6h$w*(9XXpir8=PbrXRA{<8L9(q8f-A OEvuhNOPZN0zw!?x&w1Mb literal 0 HcmV?d00001 diff --git a/OneCprogsite/programmer/utils/email_notifications.py b/OneCprogsite/programmer/utils/email_notifications.py new file mode 100644 index 0000000..fa902a8 --- /dev/null +++ b/OneCprogsite/programmer/utils/email_notifications.py @@ -0,0 +1,65 @@ +# programmer/utils/email_notifications.py +from django.core.mail import send_mail, EmailMultiAlternatives +from django.template.loader import render_to_string +from django.conf import settings +from django.utils.html import strip_tags +import logging + +logger = logging.getLogger(__name__) + + +def send_callback_notification(callback_request): + """ + Отправляет уведомление о новой заявке на обратный звонок + """ + try: + subject = f'🚨 Новая заявка на обратный звонок от {callback_request.name}' + + # HTML версия письма + html_message = render_to_string('emails/callback_notification.html', { + 'callback': callback_request, + 'site_url': settings.ALLOWED_HOSTS[0] if settings.ALLOWED_HOSTS else 'localhost', + }) + + # Текстовая версия письма + plain_message = strip_tags(html_message) + + # Проверяем настройки email + if not all([settings.EMAIL_HOST_USER, settings.EMAIL_HOST_PASSWORD]): + logger.error("Email settings are not configured properly") + return False + + # Отправляем email + send_mail( + subject=subject, + message=plain_message, + from_email=settings.DEFAULT_FROM_EMAIL, + recipient_list=settings.ADMIN_EMAILS, + html_message=html_message, + fail_silently=False, + ) + + logger.info(f"Email notification sent successfully for callback #{callback_request.id}") + return True + + except Exception as e: + logger.error(f"Error sending email notification: {e}") + return False + + +def send_test_email(): + """ + Функция для тестирования отправки email + """ + try: + send_mail( + subject='📧 Test Email from Django', + message='This is a test email from your Django application.', + from_email=settings.DEFAULT_FROM_EMAIL, + recipient_list=settings.ADMIN_EMAILS, + fail_silently=False, + ) + return True + except Exception as e: + logger.error(f"Test email failed: {e}") + return False