djresttoolkit 0.1.0__py3-none-any.whl → 0.2.0__py3-none-any.whl
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.
- README.md +1 -1
- {djresttoolkit-0.1.0.dist-info → djresttoolkit-0.2.0.dist-info}/METADATA +7 -4
- djresttoolkit-0.2.0.dist-info/RECORD +17 -0
- src/djresttoolkit/__init__.py +0 -2
- src/djresttoolkit/admin.py +0 -0
- src/djresttoolkit/apps.py +6 -0
- src/djresttoolkit/mail/__init__.py +11 -0
- src/djresttoolkit/mail/_email_sender.py +94 -0
- src/djresttoolkit/mail/_models.py +53 -0
- src/djresttoolkit/mail/_types.py +28 -0
- src/djresttoolkit/migrations/__init__.py +0 -0
- src/djresttoolkit/models/__init__.py +0 -0
- djresttoolkit-0.1.0.dist-info/RECORD +0 -9
- {djresttoolkit-0.1.0.dist-info → djresttoolkit-0.2.0.dist-info}/WHEEL +0 -0
- {djresttoolkit-0.1.0.dist-info → djresttoolkit-0.2.0.dist-info}/entry_points.txt +0 -0
- {djresttoolkit-0.1.0.dist-info → djresttoolkit-0.2.0.dist-info}/licenses/LICENSE +0 -0
README.md
CHANGED
@@ -46,4 +46,4 @@ MIT License — See [LICENSE](LICENSE).
|
|
46
46
|
|
47
47
|
## 👤 Author
|
48
48
|
|
49
|
-
For questions or assistance, contact **Shailesh** at [shaileshpandit141@gmail.com](mailto:shaileshpandit141@gmail.com).
|
49
|
+
For questions or assistance, contact **Shailesh** at [shaileshpandit141@gmail.com](mailto:shaileshpandit141@gmail.com).
|
@@ -1,10 +1,12 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: djresttoolkit
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.2.0
|
4
4
|
Summary: A collection of Django and DRF utilities to simplify API development.
|
5
5
|
Project-URL: Homepage, https://github.com/shaileshpandit141/djresttoolkit
|
6
|
-
Project-URL:
|
6
|
+
Project-URL: Documentation, https://shaileshpandit141.github.io/djresttoolkit
|
7
|
+
Project-URL: Source, https://github.com/shaileshpandit141/djresttoolkit
|
7
8
|
Project-URL: Issues, https://github.com/shaileshpandit141/djresttoolkit/issues
|
9
|
+
Project-URL: License, https://github.com/shaileshpandit141/djresttoolkit/blob/main/LICENSE
|
8
10
|
Author-email: shaileshpandit141 <shaileshpandit141@gmail.com>
|
9
11
|
License: # MIT License
|
10
12
|
|
@@ -31,7 +33,7 @@ License: # MIT License
|
|
31
33
|
OTHER DEALINGS IN THE SOFTWARE.
|
32
34
|
License-File: LICENSE
|
33
35
|
Keywords: api-development,django,django-authentication,django-email,django-helpers,django-mixins,django-pagination,django-rest,django-rest-framework-utilities,django-rest-utilities,django-serializers,django-shortcuts,django-utilities,djangorestframework,drf,python-package,rest-api
|
34
|
-
Classifier: Development Status ::
|
36
|
+
Classifier: Development Status :: 3 - Alpha
|
35
37
|
Classifier: Framework :: Django
|
36
38
|
Classifier: Framework :: Django :: 4.0
|
37
39
|
Classifier: Intended Audience :: Developers
|
@@ -44,6 +46,7 @@ Classifier: Topic :: Software Development :: Libraries
|
|
44
46
|
Classifier: Topic :: Utilities
|
45
47
|
Classifier: Typing :: Typed
|
46
48
|
Requires-Python: >=3.13
|
49
|
+
Requires-Dist: pydantic>=2.11.7
|
47
50
|
Provides-Extra: dev
|
48
51
|
Requires-Dist: mypy; extra == 'dev'
|
49
52
|
Requires-Dist: pytest; extra == 'dev'
|
@@ -98,4 +101,4 @@ MIT License — See [LICENSE](LICENSE).
|
|
98
101
|
|
99
102
|
## 👤 Author
|
100
103
|
|
101
|
-
For questions or assistance, contact **Shailesh** at [shaileshpandit141@gmail.com](mailto:shaileshpandit141@gmail.com).
|
104
|
+
For questions or assistance, contact **Shailesh** at [shaileshpandit141@gmail.com](mailto:shaileshpandit141@gmail.com).
|
@@ -0,0 +1,17 @@
|
|
1
|
+
LICENSE,sha256=8-oZM3yuuTRjySMbVKX9YXYA7Y4M_KhQNBYXPFjeWUo,1074
|
2
|
+
README.md,sha256=P0hcRXIpkEZZIiyWKsvuCkOIlqPxQ4tRkdb5R_mTARQ,1464
|
3
|
+
src/djresttoolkit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
+
src/djresttoolkit/admin.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
+
src/djresttoolkit/apps.py,sha256=nKb5GUIEhAB3IL3lTmEXNc5XuvvaZupH-1CCuYKFrEQ,158
|
6
|
+
src/djresttoolkit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
+
src/djresttoolkit/mail/__init__.py,sha256=tB9SdMlhfWQ640q4aobZ0H1c7fTWalpDL2I-onkr2VI,268
|
8
|
+
src/djresttoolkit/mail/_email_sender.py,sha256=vvTPZzSAfX2FXHv1IY0ST8dEsq8M4wAxkihm0JbRh1Y,3136
|
9
|
+
src/djresttoolkit/mail/_models.py,sha256=_41pH3xC0jP8SHSty2FkxvRh2_ddKk-4peT11OHcBBE,1462
|
10
|
+
src/djresttoolkit/mail/_types.py,sha256=est1mrN80vB_4-j-2yuAr_l_7rNzO4AJlqpq74zO5ow,682
|
11
|
+
src/djresttoolkit/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
+
src/djresttoolkit/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
|
+
djresttoolkit-0.2.0.dist-info/METADATA,sha256=Xr6JLGRsN0NlwnQ_Nu0HcdBv6Sfs7RKeIdYOq4GVoL8,4374
|
14
|
+
djresttoolkit-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
15
|
+
djresttoolkit-0.2.0.dist-info/entry_points.txt,sha256=YMhfTF-7mYppO8QqqWnvR_hyMWvoYxD6XI94_ViFu3k,60
|
16
|
+
djresttoolkit-0.2.0.dist-info/licenses/LICENSE,sha256=8-oZM3yuuTRjySMbVKX9YXYA7Y4M_KhQNBYXPFjeWUo,1074
|
17
|
+
djresttoolkit-0.2.0.dist-info/RECORD,,
|
src/djresttoolkit/__init__.py
CHANGED
File without changes
|
@@ -0,0 +1,11 @@
|
|
1
|
+
from ._email_sender import EmailSender
|
2
|
+
from ._models import EmailContent, EmailTemplate
|
3
|
+
from ._types import EmailContentDict, EmailTemplateDict
|
4
|
+
|
5
|
+
__all__ = [
|
6
|
+
"EmailSender",
|
7
|
+
"EmailContent",
|
8
|
+
"EmailTemplate",
|
9
|
+
"EmailContentDict",
|
10
|
+
"EmailTemplateDict",
|
11
|
+
]
|
@@ -0,0 +1,94 @@
|
|
1
|
+
import logging
|
2
|
+
from smtplib import SMTPException
|
3
|
+
from typing import cast
|
4
|
+
|
5
|
+
from django.core.exceptions import ValidationError
|
6
|
+
from django.core.mail import EmailMultiAlternatives
|
7
|
+
from django.template.loader import render_to_string
|
8
|
+
from pydantic import EmailStr
|
9
|
+
|
10
|
+
from ._models import EmailContent
|
11
|
+
from ._types import EmailContentDict
|
12
|
+
|
13
|
+
# Set up logger
|
14
|
+
logger = logging.getLogger(__name__)
|
15
|
+
|
16
|
+
|
17
|
+
class EmailSender:
|
18
|
+
"""
|
19
|
+
Sends templated emails using Django's EmailMultiAlternatives.
|
20
|
+
|
21
|
+
Supports:
|
22
|
+
- Plain text and HTML templates
|
23
|
+
- Template rendering with context
|
24
|
+
- Optional silent failure handling via `exceptions`
|
25
|
+
|
26
|
+
Parameters
|
27
|
+
----------
|
28
|
+
email_content : EmailContent | EmailContentDict
|
29
|
+
The email data, including subject, sender, templates, and context.
|
30
|
+
|
31
|
+
Methods
|
32
|
+
-------
|
33
|
+
send(to: list[EmailStr], exceptions: bool = False) -> bool
|
34
|
+
Sends the email to the given recipients.
|
35
|
+
- `exceptions=True` riase exceptions on failure and returns False if `exceptions=False`.
|
36
|
+
|
37
|
+
Example
|
38
|
+
-------
|
39
|
+
>>> from mypackage._models import EmailContent, EmailTemplate
|
40
|
+
>>> content = EmailContent(
|
41
|
+
... subject="Hello",
|
42
|
+
... from_email="noreply@example.com",
|
43
|
+
... context={"username": "Alice"},
|
44
|
+
... template=EmailTemplate(text="emails/welcome.txt", html="emails/welcome.html")
|
45
|
+
... )
|
46
|
+
>>> sender = EmailSender(content)
|
47
|
+
>>> sender.send(to=["user@example.com"]) # --> True/False
|
48
|
+
|
49
|
+
"""
|
50
|
+
|
51
|
+
def __init__(
|
52
|
+
self,
|
53
|
+
email_content: EmailContent | EmailContentDict,
|
54
|
+
) -> None:
|
55
|
+
"""Initialize email sender class."""
|
56
|
+
self._email_content = email_content
|
57
|
+
|
58
|
+
@property
|
59
|
+
def email_content(self) -> EmailContentDict:
|
60
|
+
"""Convert pydantic mode to python dict."""
|
61
|
+
if isinstance(self.email_content, EmailContent):
|
62
|
+
return cast(EmailContentDict, self.email_content.model_dump())
|
63
|
+
return self.email_content
|
64
|
+
|
65
|
+
def send(
|
66
|
+
self,
|
67
|
+
to: list[EmailStr],
|
68
|
+
exceptions: bool = False,
|
69
|
+
) -> bool:
|
70
|
+
"""Send email to recipients."""
|
71
|
+
unique_recipients = list(set(to))
|
72
|
+
try:
|
73
|
+
logger.info("Starting email sending process.")
|
74
|
+
email = EmailMultiAlternatives(
|
75
|
+
subject=self.email_content["subject"],
|
76
|
+
body=render_to_string(
|
77
|
+
self.email_content["template"]["text"],
|
78
|
+
self.email_content["context"],
|
79
|
+
),
|
80
|
+
from_email=self.email_content["from_email"],
|
81
|
+
to=unique_recipients,
|
82
|
+
)
|
83
|
+
email.attach_alternative(
|
84
|
+
content=self.email_content["template"]["html"],
|
85
|
+
mimetype="text/html",
|
86
|
+
)
|
87
|
+
email.send()
|
88
|
+
logger.info(f"Email sent successfully to: {', '.join(unique_recipients)}")
|
89
|
+
return True
|
90
|
+
except (SMTPException, ValidationError) as error:
|
91
|
+
logger.error(f"Error during sending email\nErrors: {error}")
|
92
|
+
if exceptions:
|
93
|
+
raise ValidationError("Error during sending email") from error
|
94
|
+
return False
|
@@ -0,0 +1,53 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
from pydantic import BaseModel, EmailStr, field_validator
|
4
|
+
|
5
|
+
|
6
|
+
class EmailTemplate(BaseModel):
|
7
|
+
"""Template for rendering email content."""
|
8
|
+
|
9
|
+
text: str # plain text version
|
10
|
+
html: str # HTML version
|
11
|
+
|
12
|
+
@field_validator("text")
|
13
|
+
@classmethod
|
14
|
+
def validate_text_template(cls, v: str) -> str:
|
15
|
+
if not v.endswith(".txt"):
|
16
|
+
raise ValueError("Text template must end with .txt")
|
17
|
+
return v
|
18
|
+
|
19
|
+
@field_validator("html")
|
20
|
+
@classmethod
|
21
|
+
def validate_html_template(cls, v: str) -> str:
|
22
|
+
if not v.endswith(".html"):
|
23
|
+
raise ValueError("HTML template must end with .html")
|
24
|
+
return v
|
25
|
+
|
26
|
+
|
27
|
+
class EmailContent(BaseModel):
|
28
|
+
"""
|
29
|
+
Represents the content of an email ready for rendering.
|
30
|
+
|
31
|
+
This model combines the key parts of an email, including:
|
32
|
+
- subject line
|
33
|
+
- sender email (from_email)
|
34
|
+
- rendering context for template variables
|
35
|
+
- associated text and/or HTML templates
|
36
|
+
|
37
|
+
Attributes
|
38
|
+
----------
|
39
|
+
subject : str
|
40
|
+
The subject line of the email.
|
41
|
+
from_email : EmailStr | None
|
42
|
+
Sender's email address.
|
43
|
+
context : dict[str, Any] | None
|
44
|
+
Optional context data used to render templates (e.g., {"username": "Alice"}).
|
45
|
+
template : EmailTemplate
|
46
|
+
Templates for the email body (.txt and/or .html).
|
47
|
+
|
48
|
+
"""
|
49
|
+
|
50
|
+
subject: str
|
51
|
+
from_email: EmailStr | None
|
52
|
+
context: dict[str, Any] | None = None
|
53
|
+
template: EmailTemplate
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from typing import Any, TypedDict
|
2
|
+
|
3
|
+
from pydantic import EmailStr
|
4
|
+
|
5
|
+
|
6
|
+
class EmailTemplateDict(TypedDict):
|
7
|
+
"""Template for rendering email content."""
|
8
|
+
|
9
|
+
text: str # plain text version
|
10
|
+
html: str # HTML version html: str # HTML version
|
11
|
+
|
12
|
+
|
13
|
+
class EmailContentDict(TypedDict):
|
14
|
+
"""
|
15
|
+
Represents a fully configured email message.
|
16
|
+
|
17
|
+
This model combines the essential parts of an email, including:
|
18
|
+
- subject line
|
19
|
+
- from_email the sender email
|
20
|
+
- a rendering context for template variables
|
21
|
+
- associated text and/or HTML templates
|
22
|
+
|
23
|
+
"""
|
24
|
+
|
25
|
+
subject: str
|
26
|
+
from_email: EmailStr | None
|
27
|
+
context: dict[str, Any] | None
|
28
|
+
template: EmailTemplateDict
|
File without changes
|
File without changes
|
@@ -1,9 +0,0 @@
|
|
1
|
-
LICENSE,sha256=8-oZM3yuuTRjySMbVKX9YXYA7Y4M_KhQNBYXPFjeWUo,1074
|
2
|
-
README.md,sha256=Zk4_MIYW-Wr07HnFOANa3uQXNA08dPdQaNJhjcUH07w,1463
|
3
|
-
src/djresttoolkit/__init__.py,sha256=ENteKzaWCshHx06bXsudOKG4J4PqQrCELdx4Gx6p7hU,53
|
4
|
-
src/djresttoolkit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
djresttoolkit-0.1.0.dist-info/METADATA,sha256=yCgqJyTEOVrSvKHKRpLApPIMJh7vHSuBbUumuUOpG-A,4179
|
6
|
-
djresttoolkit-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
7
|
-
djresttoolkit-0.1.0.dist-info/entry_points.txt,sha256=YMhfTF-7mYppO8QqqWnvR_hyMWvoYxD6XI94_ViFu3k,60
|
8
|
-
djresttoolkit-0.1.0.dist-info/licenses/LICENSE,sha256=8-oZM3yuuTRjySMbVKX9YXYA7Y4M_KhQNBYXPFjeWUo,1074
|
9
|
-
djresttoolkit-0.1.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|