notification-kit 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. notification_kit-1.0.0/PKG-INFO +132 -0
  2. notification_kit-1.0.0/README.md +105 -0
  3. notification_kit-1.0.0/notification_kit/__init__.py +5 -0
  4. notification_kit-1.0.0/notification_kit/admin.py +137 -0
  5. notification_kit-1.0.0/notification_kit/api/__init__.py +0 -0
  6. notification_kit-1.0.0/notification_kit/api/permissions.py +16 -0
  7. notification_kit-1.0.0/notification_kit/api/serializers.py +12 -0
  8. notification_kit-1.0.0/notification_kit/api/urls.py +16 -0
  9. notification_kit-1.0.0/notification_kit/api/views.py +85 -0
  10. notification_kit-1.0.0/notification_kit/apps.py +7 -0
  11. notification_kit-1.0.0/notification_kit/backends/__init__.py +0 -0
  12. notification_kit-1.0.0/notification_kit/backends/base.py +136 -0
  13. notification_kit-1.0.0/notification_kit/backends/custom.py +21 -0
  14. notification_kit-1.0.0/notification_kit/backends/email.py +102 -0
  15. notification_kit-1.0.0/notification_kit/backends/push.py +51 -0
  16. notification_kit-1.0.0/notification_kit/backends/sms.py +45 -0
  17. notification_kit-1.0.0/notification_kit/exceptions.py +168 -0
  18. notification_kit-1.0.0/notification_kit/middleware.py +29 -0
  19. notification_kit-1.0.0/notification_kit/models/__init__.py +26 -0
  20. notification_kit-1.0.0/notification_kit/models/abstract.py +287 -0
  21. notification_kit-1.0.0/notification_kit/models/concrete.py +59 -0
  22. notification_kit-1.0.0/notification_kit/models/mixins.py +86 -0
  23. notification_kit-1.0.0/notification_kit/services/__init__.py +0 -0
  24. notification_kit-1.0.0/notification_kit/services/base.py +97 -0
  25. notification_kit-1.0.0/notification_kit/services/custom_service.py +19 -0
  26. notification_kit-1.0.0/notification_kit/settings.py +218 -0
  27. notification_kit-1.0.0/notification_kit/signals.py +118 -0
  28. notification_kit-1.0.0/notification_kit/tasks/__init__.py +0 -0
  29. notification_kit-1.0.0/notification_kit/tasks/base.py +138 -0
  30. notification_kit-1.0.0/notification_kit/utils/__init__.py +0 -0
  31. notification_kit-1.0.0/notification_kit/utils/template.py +60 -0
  32. notification_kit-1.0.0/notification_kit/utils/validators.py +61 -0
  33. notification_kit-1.0.0/pyproject.toml +47 -0
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: notification-kit
3
+ Version: 1.0.0
4
+ Summary: Reusable, extensible Django notification framework
5
+ License: MIT
6
+ Keywords: django,notifications,email,sms,push,celery,django-app
7
+ Author: Sercan Tatar
8
+ Author-email: sercan@everionconsulting.com
9
+ Requires-Python: >=3.10,<4.0
10
+ Classifier: Framework :: Django
11
+ Classifier: Framework :: Django :: 4.2
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Requires-Dist: celery (>=5.3,<6.0)
21
+ Requires-Dist: django (>=4.2)
22
+ Requires-Dist: djangorestframework (>=3.14,<4.0)
23
+ Project-URL: Documentation, https://github.com/gulergokce/django-notification-kit
24
+ Project-URL: Homepage, https://github.com/gulergokce/django-notification-kit
25
+ Project-URL: Repository, https://github.com/gulergokce/django-notification-kit
26
+ Description-Content-Type: text/markdown
27
+
28
+ 🚀 Django Notification Kit
29
+ Django Notification Kit, Django projeleri için tasarlanmış; çok kanallı (Email / SMS / Push) bildirim gönderimini yöneten, modüler ve genişletilebilir bir altyapıdır. Sistemin temel amacı; bildirim gönderim süreçlerini API – Service – Backend – Task katmanlarına ayırarak yüksek test edilebilirlik ve sürdürülebilirlik sağlamaktır.
30
+
31
+
32
+
33
+ 🎯 Kapsam (v1.0.0 – MVP)
34
+ Bu versiyon, profesyonel bir bildirim sistemi için gerekli olan şu temel bileşenleri sunar:
35
+
36
+ ✅ Abstract Model Yapısı: Genişletilmeye hazır taban modeller.
37
+
38
+ ✅ Concrete Modeller: Notification, Template, Preference ve Log yapıları.
39
+
40
+ ✅ SMTP Email Backend: Standart e-posta gönderim desteği.
41
+
42
+ ✅ Base Notification Service: İş mantığını yöneten merkezi servis.
43
+
44
+ ✅ Celery Task Factory: Ölçeklenebilir, asenkron gönderim desteği.
45
+
46
+ ✅ DRF API: Standartlara uygun API uçları ve Serializer'lar.
47
+
48
+ ✅ Merkezi Ayar Yönetimi: NOTIFICATION_KIT üzerinden tam kontrol.
49
+
50
+ ✅ Full Testing: Unit, API ve Performans testleri.
51
+
52
+
53
+
54
+ 🏛 Mimari Genel Bakış
55
+ Sistem, sorumlulukların net ayrıştırıldığı Layered Architecture (Katmanlı Mimari) prensibiyle tasarlanmıştır.Katmanların SorumluluklarıKatmanSorumluluk AlanıAPIRequest validation, permission kontrolü, throttling.Serviceİş kuralları, kanal seçimi, retry logic yönetimi.BackendKanal spesifik (Email, SMS vb.) düşük seviyeli implementasyon.TaskAsenkron (Async) ve toplu (Batch) işlem yönetimi (Celery).ModelsVeri şeması; Notification, Template ve Kullanıcı tercihleri.SettingsMerkezi ve projeye özel override edilebilir konfigürasyon.
56
+
57
+
58
+
59
+ ⚙️ Ayar Yönetimi
60
+ Paket içi ayarlar notification_kit/settings.py dosyasında tanımlıdır. Proje bazlı özelleştirmeler için ana settings.py dosyanızda NOTIFICATION_KIT sözlüğünü kullanabilirsiniz.
61
+
62
+ Python
63
+ # project/settings.py
64
+
65
+ NOTIFICATION_KIT = {
66
+ "ASYNC": True,
67
+ "MAX_RETRIES": 5,
68
+ "EMAIL": {
69
+ "FROM_EMAIL": "noreply@example.com",
70
+ "RATE_LIMIT": 200,
71
+ },
72
+ }
73
+ Not: Hassas veriler (API Key, Şifre vb.) her zaman çevre değişkenleri (environment variables) üzerinden yönetilmelidir.
74
+
75
+
76
+
77
+ 🛠 Teknik Implementasyon
78
+ 1. Backend Yapısı
79
+ Her kanal (SMS, Email, Push) için bir backend sınıfı bulunur. Yeni bir sağlayıcı eklemek oldukça basittir:
80
+
81
+ Python
82
+ class BaseBackend:
83
+ def send(self, notification):
84
+ raise NotImplementedError("Bu metod implemente edilmelidir.")
85
+ 2. Service Katmanı
86
+ İş mantığının kalbidir. API'den gelen isteği alır, hangi kanalın kullanılacağına karar verir ve gerekirse işi Celery'ye devreder.
87
+
88
+ Kanal Seçimi: Template üzerinden dinamik kanal yönetimi.
89
+
90
+ Soyutlama: Backend implementasyonundan bağımsız çalışma.
91
+
92
+ 3. API Katmanı
93
+ Yalnızca bildirim oluşturma ve tetikleme sorumluluğunu taşır.
94
+
95
+ Validation: Serializer'lar ile veri doğruluğu garanti edilir.
96
+
97
+ Security: Permission sınıfları ile yetkisiz erişim engellenir.
98
+
99
+
100
+
101
+ 🔒 Güvenlik Yaklaşımı
102
+ Veri Gizliliği: Kişisel veriler (PII) kesinlikle loglanmaz.
103
+
104
+ Hız Sınırı: API düzeyinde Throttling ile brute-force ve aşırı yüklenme engellenir.
105
+
106
+ Denetim: Tüm gönderim denemeleri ve sonuçları NotificationLog üzerinden izlenebilir.
107
+
108
+
109
+
110
+ 🧪 Test Stratejisi
111
+ Sistem, Pytest altyapısı kullanılarak aşağıdaki seviyelerde test edilmektedir:
112
+
113
+ Model: Veri bütünlüğü ve ilişki testleri.
114
+
115
+ Service: İş mantığı ve kanal yönlendirme testleri.
116
+
117
+ Backend: Provider entegrasyon (mock) testleri.
118
+
119
+ API: Endpoint erişim ve veri formatı testleri.
120
+
121
+
122
+
123
+ 🤝 Katkıda Bulunma
124
+ Projeyi fork edin.
125
+
126
+ Yeni bir feature branch oluşturun (git checkout -b feature/yeniOzellik).
127
+
128
+ Değişikliklerinizi commit edin (git commit -m 'Ekle: Yeni SMS Backend').
129
+
130
+ Branch'inizi push edin (git push origin feature/yeniOzellik).
131
+
132
+ Bir Pull Request açın.
@@ -0,0 +1,105 @@
1
+ 🚀 Django Notification Kit
2
+ Django Notification Kit, Django projeleri için tasarlanmış; çok kanallı (Email / SMS / Push) bildirim gönderimini yöneten, modüler ve genişletilebilir bir altyapıdır. Sistemin temel amacı; bildirim gönderim süreçlerini API – Service – Backend – Task katmanlarına ayırarak yüksek test edilebilirlik ve sürdürülebilirlik sağlamaktır.
3
+
4
+
5
+
6
+ 🎯 Kapsam (v1.0.0 – MVP)
7
+ Bu versiyon, profesyonel bir bildirim sistemi için gerekli olan şu temel bileşenleri sunar:
8
+
9
+ ✅ Abstract Model Yapısı: Genişletilmeye hazır taban modeller.
10
+
11
+ ✅ Concrete Modeller: Notification, Template, Preference ve Log yapıları.
12
+
13
+ ✅ SMTP Email Backend: Standart e-posta gönderim desteği.
14
+
15
+ ✅ Base Notification Service: İş mantığını yöneten merkezi servis.
16
+
17
+ ✅ Celery Task Factory: Ölçeklenebilir, asenkron gönderim desteği.
18
+
19
+ ✅ DRF API: Standartlara uygun API uçları ve Serializer'lar.
20
+
21
+ ✅ Merkezi Ayar Yönetimi: NOTIFICATION_KIT üzerinden tam kontrol.
22
+
23
+ ✅ Full Testing: Unit, API ve Performans testleri.
24
+
25
+
26
+
27
+ 🏛 Mimari Genel Bakış
28
+ Sistem, sorumlulukların net ayrıştırıldığı Layered Architecture (Katmanlı Mimari) prensibiyle tasarlanmıştır.Katmanların SorumluluklarıKatmanSorumluluk AlanıAPIRequest validation, permission kontrolü, throttling.Serviceİş kuralları, kanal seçimi, retry logic yönetimi.BackendKanal spesifik (Email, SMS vb.) düşük seviyeli implementasyon.TaskAsenkron (Async) ve toplu (Batch) işlem yönetimi (Celery).ModelsVeri şeması; Notification, Template ve Kullanıcı tercihleri.SettingsMerkezi ve projeye özel override edilebilir konfigürasyon.
29
+
30
+
31
+
32
+ ⚙️ Ayar Yönetimi
33
+ Paket içi ayarlar notification_kit/settings.py dosyasında tanımlıdır. Proje bazlı özelleştirmeler için ana settings.py dosyanızda NOTIFICATION_KIT sözlüğünü kullanabilirsiniz.
34
+
35
+ Python
36
+ # project/settings.py
37
+
38
+ NOTIFICATION_KIT = {
39
+ "ASYNC": True,
40
+ "MAX_RETRIES": 5,
41
+ "EMAIL": {
42
+ "FROM_EMAIL": "noreply@example.com",
43
+ "RATE_LIMIT": 200,
44
+ },
45
+ }
46
+ Not: Hassas veriler (API Key, Şifre vb.) her zaman çevre değişkenleri (environment variables) üzerinden yönetilmelidir.
47
+
48
+
49
+
50
+ 🛠 Teknik Implementasyon
51
+ 1. Backend Yapısı
52
+ Her kanal (SMS, Email, Push) için bir backend sınıfı bulunur. Yeni bir sağlayıcı eklemek oldukça basittir:
53
+
54
+ Python
55
+ class BaseBackend:
56
+ def send(self, notification):
57
+ raise NotImplementedError("Bu metod implemente edilmelidir.")
58
+ 2. Service Katmanı
59
+ İş mantığının kalbidir. API'den gelen isteği alır, hangi kanalın kullanılacağına karar verir ve gerekirse işi Celery'ye devreder.
60
+
61
+ Kanal Seçimi: Template üzerinden dinamik kanal yönetimi.
62
+
63
+ Soyutlama: Backend implementasyonundan bağımsız çalışma.
64
+
65
+ 3. API Katmanı
66
+ Yalnızca bildirim oluşturma ve tetikleme sorumluluğunu taşır.
67
+
68
+ Validation: Serializer'lar ile veri doğruluğu garanti edilir.
69
+
70
+ Security: Permission sınıfları ile yetkisiz erişim engellenir.
71
+
72
+
73
+
74
+ 🔒 Güvenlik Yaklaşımı
75
+ Veri Gizliliği: Kişisel veriler (PII) kesinlikle loglanmaz.
76
+
77
+ Hız Sınırı: API düzeyinde Throttling ile brute-force ve aşırı yüklenme engellenir.
78
+
79
+ Denetim: Tüm gönderim denemeleri ve sonuçları NotificationLog üzerinden izlenebilir.
80
+
81
+
82
+
83
+ 🧪 Test Stratejisi
84
+ Sistem, Pytest altyapısı kullanılarak aşağıdaki seviyelerde test edilmektedir:
85
+
86
+ Model: Veri bütünlüğü ve ilişki testleri.
87
+
88
+ Service: İş mantığı ve kanal yönlendirme testleri.
89
+
90
+ Backend: Provider entegrasyon (mock) testleri.
91
+
92
+ API: Endpoint erişim ve veri formatı testleri.
93
+
94
+
95
+
96
+ 🤝 Katkıda Bulunma
97
+ Projeyi fork edin.
98
+
99
+ Yeni bir feature branch oluşturun (git checkout -b feature/yeniOzellik).
100
+
101
+ Değişikliklerinizi commit edin (git commit -m 'Ekle: Yeni SMS Backend').
102
+
103
+ Branch'inizi push edin (git push origin feature/yeniOzellik).
104
+
105
+ Bir Pull Request açın.
@@ -0,0 +1,5 @@
1
+ """
2
+ Reusable Django Notification Kit
3
+ """
4
+
5
+ __version__ = "1.0.0"
@@ -0,0 +1,137 @@
1
+ from django.contrib import admin
2
+
3
+ from notification_kit.models.concrete import (
4
+ Notification,
5
+ NotificationTemplate,
6
+ NotificationPreference,
7
+ NotificationLog,
8
+ )
9
+
10
+
11
+ @admin.register(Notification)
12
+ class NotificationAdmin(admin.ModelAdmin):
13
+ """
14
+ Ana Notification modeli için admin ayarları
15
+ """
16
+
17
+ list_display = (
18
+ "id",
19
+ "user",
20
+ "channel",
21
+ "status",
22
+ "priority",
23
+ "created_at",
24
+ )
25
+
26
+ list_filter = (
27
+ "channel",
28
+ "status",
29
+ "priority",
30
+ )
31
+
32
+ search_fields = (
33
+ "title",
34
+ "body",
35
+ "user__username",
36
+ "user__email",
37
+ )
38
+
39
+ readonly_fields = (
40
+ "sent_at",
41
+ "read_at",
42
+ "created_at",
43
+ "updated_at",
44
+ )
45
+
46
+ ordering = ("-created_at",)
47
+
48
+
49
+ @admin.register(NotificationTemplate)
50
+ class NotificationTemplateAdmin(admin.ModelAdmin):
51
+ """
52
+ Bildirim şablonları için admin ayarları
53
+ """
54
+
55
+ list_display = (
56
+ "code",
57
+ "name",
58
+ "channel",
59
+ "is_active",
60
+ )
61
+
62
+ list_filter = (
63
+ "channel",
64
+ "is_active",
65
+ )
66
+
67
+ search_fields = (
68
+ "code",
69
+ "name",
70
+ "description",
71
+ )
72
+
73
+ ordering = ("code",)
74
+
75
+
76
+ @admin.register(NotificationPreference)
77
+ class NotificationPreferenceAdmin(admin.ModelAdmin):
78
+ """
79
+ Kullanıcı bildirim tercihleri admin ayarları
80
+ """
81
+
82
+ list_display = (
83
+ "id",
84
+ "user",
85
+ "channel",
86
+ "is_enabled",
87
+ "frequency",
88
+ )
89
+
90
+ list_filter = (
91
+ "channel",
92
+ "is_enabled",
93
+ "frequency",
94
+ )
95
+
96
+ search_fields = (
97
+ "user__username",
98
+ "user__email",
99
+ )
100
+
101
+
102
+ @admin.register(NotificationLog)
103
+ class NotificationLogAdmin(admin.ModelAdmin):
104
+ """
105
+ Bildirim logları admin ayarları
106
+ """
107
+
108
+ list_display = (
109
+ "notification_id",
110
+ "action",
111
+ "old_status",
112
+ "new_status",
113
+ "created_at",
114
+ )
115
+
116
+ list_filter = (
117
+ "action",
118
+ "created_at",
119
+ )
120
+
121
+ search_fields = (
122
+ "notification_id",
123
+ "action",
124
+ )
125
+
126
+ readonly_fields = (
127
+ "notification_id",
128
+ "action",
129
+ "old_status",
130
+ "new_status",
131
+ "details",
132
+ "ip_address",
133
+ "user_agent",
134
+ "created_at",
135
+ )
136
+
137
+ ordering = ("-created_at",)
@@ -0,0 +1,16 @@
1
+ from rest_framework.permissions import BasePermission, SAFE_METHODS
2
+
3
+
4
+ class IsAdminOrReadOnly(BasePermission):
5
+ """
6
+ Okuma işlemleri herkese açık olabilir.
7
+ Yazma / silme işlemleri sadece admin (staff) kullanıcıya izinlidir.
8
+ """
9
+
10
+ def has_permission(self, request, view):
11
+ # GET, HEAD, OPTIONS serbest
12
+ if request.method in SAFE_METHODS:
13
+ return True
14
+
15
+ # Yazma işlemleri için admin şartı
16
+ return bool(request.user and request.user.is_staff)
@@ -0,0 +1,12 @@
1
+ from rest_framework import serializers
2
+ from ..models.concrete import Notification, NotificationPreference
3
+
4
+ class NotificationSerializer(serializers.ModelSerializer):
5
+ class Meta:
6
+ model = Notification
7
+ fields = "__all__"
8
+
9
+ class NotificationPreferenceSerializer(serializers.ModelSerializer):
10
+ class Meta:
11
+ model = NotificationPreference
12
+ fields = "__all__"
@@ -0,0 +1,16 @@
1
+ from django.urls import path, include
2
+ from rest_framework.routers import DefaultRouter
3
+ from .views import NotificationViewSet, NotificationPreferenceViewSet, admin_send_notification
4
+
5
+ router = DefaultRouter()
6
+ router.register(r"notifications", NotificationViewSet, basename="notification")
7
+ router.register(r"preferences", NotificationPreferenceViewSet, basename="preference")
8
+
9
+ urlpatterns = [
10
+ path("api/", include(router.urls)),
11
+ path(
12
+ "admin/notifications/send/",
13
+ admin_send_notification,
14
+ name="admin-notifications-send",
15
+ ),
16
+ ]
@@ -0,0 +1,85 @@
1
+ from rest_framework import viewsets, status
2
+ from rest_framework.decorators import action
3
+ from rest_framework.response import Response
4
+ from .serializers import NotificationSerializer, NotificationPreferenceSerializer
5
+ from ..models.concrete import Notification, NotificationPreference
6
+ from notification_kit.api.permissions import IsAdminOrReadOnly
7
+ from django.contrib.auth import get_user_model
8
+ from rest_framework.decorators import api_view, permission_classes
9
+ from rest_framework.permissions import IsAuthenticated
10
+
11
+ from ..models import Notification # concrete Notification
12
+
13
+ class NotificationViewSet(viewsets.ModelViewSet):
14
+ queryset = Notification.objects.all()
15
+ serializer_class = NotificationSerializer
16
+ permission_classes = [IsAdminOrReadOnly]
17
+
18
+ @action(detail=True, methods=["post"])
19
+ def read(self, request, pk=None):
20
+ notification = self.get_object()
21
+ notification.mark_as_read()
22
+ return Response({"status": "ok"})
23
+
24
+ @action(detail=False, methods=["post"])
25
+ def read_all(self, request):
26
+ notifications = Notification.objects.all() # filtreleme eklenebilir
27
+ for n in notifications:
28
+ n.mark_as_read()
29
+ return Response({"status": "ok"})
30
+
31
+ @action(detail=False, methods=["get"])
32
+ def unread_count(self, request):
33
+ count = Notification.objects.filter(read_at__isnull=True).count()
34
+ return Response({"unread_count": count})
35
+
36
+ class NotificationPreferenceViewSet(viewsets.ModelViewSet):
37
+ queryset = NotificationPreference.objects.all()
38
+ serializer_class = NotificationPreferenceSerializer
39
+
40
+ User = get_user_model()
41
+
42
+
43
+ @api_view(["POST"])
44
+ @permission_classes([IsAuthenticated])
45
+ def admin_send_notification(request):
46
+ """
47
+ Admin/System endpoint:
48
+ recipient_id + title + body alır, Notification kaydı üretir.
49
+ Testin aradığı url-name: admin-notifications-send
50
+ """
51
+ recipient_id = request.data.get("recipient_id")
52
+ title = request.data.get("title")
53
+ body = request.data.get("body")
54
+
55
+ if not recipient_id or not title or not body:
56
+ return Response(
57
+ {"detail": "recipient_id, title ve body zorunlu."},
58
+ status=status.HTTP_400_BAD_REQUEST,
59
+ )
60
+
61
+ try:
62
+ user = User.objects.get(id=recipient_id)
63
+ except User.DoesNotExist:
64
+ return Response(
65
+ {"detail": "recipient bulunamadı."},
66
+ status=status.HTTP_404_NOT_FOUND,
67
+ )
68
+
69
+ channel = request.data.get("channel", "email")
70
+ priority = request.data.get("priority", getattr(Notification, "PRIORITY_NORMAL", "normal"))
71
+
72
+ n = Notification.objects.create(
73
+ user=user, # <- sizin modelde NOT NULL olan alan bu
74
+ channel=channel,
75
+ status=getattr(Notification, "STATUS_PENDING", "pending"),
76
+ title=title,
77
+ body=body,
78
+ priority=priority,
79
+ metadata=request.data.get("metadata", {}),
80
+ )
81
+
82
+ return Response(
83
+ {"id": n.id, "status": n.status},
84
+ status=status.HTTP_201_CREATED,
85
+ )
@@ -0,0 +1,7 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class NotificationKitConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "notification_kit"
7
+ verbose_name = "Notification Kit"
@@ -0,0 +1,136 @@
1
+ # notification_kit/backends/base.py
2
+ from __future__ import annotations
3
+
4
+ import logging
5
+ import time
6
+ from abc import ABC
7
+ from typing import Any
8
+
9
+ from notification_kit.settings import get_settings
10
+ from notification_kit.utils.validators import mask_pii
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class BaseNotificationBackend(ABC):
16
+ """
17
+ Tüm bildirim backend'lerinin uyması gereken temel arayüz.
18
+ Email, SMS ve Push backend'leri bu sınıftan türetilmelidir.
19
+ """
20
+
21
+ name: str = ""
22
+ channel: str = ""
23
+
24
+ def __init__(self) -> None:
25
+ settings = get_settings()
26
+
27
+ self.max_retries: int = settings.get("MAX_RETRIES", 3)
28
+ self.retry_delay: int = settings.get("RETRY_DELAY", 300)
29
+
30
+ channel_settings = settings.get(self.channel.upper(), {})
31
+ self.batch_size: int = channel_settings.get("BATCH_SIZE", 1)
32
+ self.rate_limit: int = channel_settings.get("RATE_LIMIT", 60)
33
+
34
+ # -------------------------------------------------
35
+ # Interface (Varsayılan: override edilmesi beklenir)
36
+ # Not: abstract bırakmıyoruz ki testlerde DummyBackend
37
+ # rahat instantiate olabilsin.
38
+ # -------------------------------------------------
39
+ def send(self, notification: Any) -> bool:
40
+ """
41
+ Bildirimi gönderir.
42
+ Başarılıysa True, başarısızsa False döner.
43
+ """
44
+ raise NotImplementedError(f"{self.__class__.__name__}.send() implement edilmedi")
45
+
46
+ def validate_recipient(self, recipient: Any) -> bool:
47
+ """
48
+ Alıcı bilgisini doğrular.
49
+ """
50
+ raise NotImplementedError(
51
+ f"{self.__class__.__name__}.validate_recipient() implement edilmedi"
52
+ )
53
+
54
+ def get_delivery_status(self, notification: Any) -> str:
55
+ """
56
+ Bildirimin teslim durumunu sorgular.
57
+ """
58
+ raise NotImplementedError(
59
+ f"{self.__class__.__name__}.get_delivery_status() implement edilmedi"
60
+ )
61
+
62
+ # --------------------
63
+ # Ortak yardımcı metodlar
64
+ # --------------------
65
+ def format_message(self, template: str, context: dict) -> str:
66
+ """
67
+ Template ve context kullanarak mesaj içeriğini oluşturur.
68
+ (Bu format, str.format bazlıdır. Django Template değil.)
69
+ """
70
+ try:
71
+ return template.format(**context)
72
+ except KeyError as exc:
73
+ raise ValueError(f"Eksik template değişkeni: {exc}") from exc
74
+
75
+ def sanitize_content(self, content: str) -> str:
76
+ """
77
+ İçeriği güvenli hale getirir.
78
+ Zararlı karakterler ve gereksiz boşluklar temizlenir.
79
+ """
80
+ return (content or "").strip()
81
+
82
+ # --------------------
83
+ # Log + Retry helpers
84
+ # --------------------
85
+ def log_send_attempt(self, notification: Any, success: bool, error: Exception | None = None) -> None:
86
+ """
87
+ Bildirim gönderim denemesini loglar.
88
+ PII veriler maskelenerek yazılır.
89
+ """
90
+ recipient = ""
91
+ if hasattr(notification, "get_recipient_identifier"):
92
+ try:
93
+ recipient = str(notification.get_recipient_identifier())
94
+ except Exception:
95
+ recipient = ""
96
+
97
+ safe_recipient = mask_pii(recipient) if recipient else ""
98
+
99
+ payload = {
100
+ "backend": self.name or self.__class__.__name__,
101
+ "channel": self.channel,
102
+ "recipient": safe_recipient,
103
+ "success": success,
104
+ "error": str(error) if error else None,
105
+ }
106
+
107
+ # stdout yerine logger kullanalım (testlerde de temiz)
108
+ logger.info("[NOTIFICATION LOG] %s", payload)
109
+
110
+ def handle_error(self, notification: Any, error: Exception) -> None:
111
+ """
112
+ Hata durumunda loglama + notification state günceller.
113
+ """
114
+ self.log_send_attempt(notification, success=False, error=error)
115
+
116
+ if hasattr(notification, "mark_as_failed"):
117
+ try:
118
+ notification.mark_as_failed(str(error))
119
+ except Exception:
120
+ # notification mark_as_failed patlarsa testleri kırmayalım
121
+ logger.exception("mark_as_failed çağrısı hata verdi")
122
+
123
+ def should_retry(self, notification: Any, error: Exception) -> bool:
124
+ """
125
+ Bildirim tekrar denenmeli mi?
126
+ """
127
+ retry_count = getattr(notification, "retry_count", None)
128
+ if retry_count is None:
129
+ return False
130
+
131
+ if retry_count >= self.max_retries:
132
+ return False
133
+
134
+ # basit backoff (settings.RETRY_DELAY)
135
+ time.sleep(self.retry_delay)
136
+ return True
@@ -0,0 +1,21 @@
1
+ from .email import BaseSMTPBackend
2
+ from .sms import BaseSMSBackend
3
+ from .push import BasePushBackend
4
+
5
+ class EmailBackend(BaseSMTPBackend):
6
+ def build_email_message(self, notification):
7
+ message = super().build_email_message(notification)
8
+ # Proje özel footer ekle
9
+ message.body += "\n\n-- Proje Footer --"
10
+ return message
11
+
12
+ class SMSBackend(BaseSMSBackend):
13
+ def send_sms(self, phone_number, message):
14
+ # Örnek: Twilio API entegrasyonu
15
+ return super().send_sms(phone_number, message)
16
+
17
+ class PushBackend(BasePushBackend):
18
+ def build_payload(self, notification):
19
+ payload = super().build_payload(notification)
20
+ payload["custom"] = "Proje özel data"
21
+ return payload