epok-toolkit 1.11.1__py3-none-any.whl → 1.11.2__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.
Potentially problematic release.
This version of epok-toolkit might be problematic. Click here for more details.
- epok_toolkit/default_settings.py +19 -1
- epok_toolkit/django/viewsets.py +40 -25
- epok_toolkit/email/templates.py +121 -133
- epok_toolkit/pdf/ticket_pdf.py +0 -4
- epok_toolkit-1.11.2.dist-info/METADATA +75 -0
- {epok_toolkit-1.11.1.dist-info → epok_toolkit-1.11.2.dist-info}/RECORD +9 -9
- epok_toolkit-1.11.1.dist-info/METADATA +0 -25
- {epok_toolkit-1.11.1.dist-info → epok_toolkit-1.11.2.dist-info}/WHEEL +0 -0
- {epok_toolkit-1.11.1.dist-info → epok_toolkit-1.11.2.dist-info}/licenses/LICENSE +0 -0
- {epok_toolkit-1.11.1.dist-info → epok_toolkit-1.11.2.dist-info}/top_level.txt +0 -0
epok_toolkit/default_settings.py
CHANGED
|
@@ -12,4 +12,22 @@ EMAIL_HOST_PASSWORD = "your-email-password"
|
|
|
12
12
|
# Configuración de whatsApp
|
|
13
13
|
API_KEY = "your-whatsapp-api-key"
|
|
14
14
|
INSTANCE = "instance-id"
|
|
15
|
-
SERVER_URL = "https://your-server-url.com/"
|
|
15
|
+
SERVER_URL = "https://your-server-url.com/"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ---------- TEMPLATE DE CORREO ELECTRÓNICO ---------- #
|
|
20
|
+
TEMPLATES_SETTINGS = {
|
|
21
|
+
"company": {
|
|
22
|
+
"name": "Congrats",
|
|
23
|
+
"email": "info@compania.com",
|
|
24
|
+
"eslogan": "Eslogan sin definir",
|
|
25
|
+
"footer": "¡Nos vemos pronto!<br><em> El equipo de Congrats 🥳</em>"
|
|
26
|
+
},
|
|
27
|
+
"colors": {
|
|
28
|
+
"background": "#f9fafb",
|
|
29
|
+
"primary": "#4f46e5",
|
|
30
|
+
"text": "#374151",
|
|
31
|
+
"white": "#ffffff"
|
|
32
|
+
}
|
|
33
|
+
}
|
epok_toolkit/django/viewsets.py
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
# core/viewsets.py
|
|
2
2
|
from rest_framework import viewsets
|
|
3
3
|
from rest_framework.pagination import PageNumberPagination
|
|
4
|
+
from rest_framework.permissions import AllowAny
|
|
4
5
|
|
|
5
6
|
class DefaultPagination(PageNumberPagination):
|
|
7
|
+
"""
|
|
8
|
+
Clase de paginación por defecto para los ViewSets.
|
|
9
|
+
Puedes personalizarla según tus necesidades.
|
|
10
|
+
"""
|
|
6
11
|
page_size = 10
|
|
7
12
|
page_size_query_param = 'page_size'
|
|
8
13
|
max_page_size = 100
|
|
@@ -10,32 +15,42 @@ class DefaultPagination(PageNumberPagination):
|
|
|
10
15
|
|
|
11
16
|
|
|
12
17
|
class BaseOptimizedViewSet(viewsets.ModelViewSet):
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"""
|
|
17
|
-
pagination_class = DefaultPagination
|
|
18
|
+
queryset = None
|
|
19
|
+
write_serializer_class = None
|
|
20
|
+
update_serializer_class = None
|
|
18
21
|
simple_serializer_class = None
|
|
19
22
|
full_serializer_class = None
|
|
23
|
+
serializer_class = full_serializer_class
|
|
24
|
+
extensions_auto_optimize = True
|
|
25
|
+
|
|
26
|
+
permission_classes = [AllowAny]
|
|
27
|
+
|
|
28
|
+
filterset_fields = []
|
|
29
|
+
search_fields = []
|
|
30
|
+
ordering_fields = []
|
|
31
|
+
ordering = []
|
|
32
|
+
|
|
33
|
+
def get_queryset(self):
|
|
34
|
+
qs = super().get_queryset()
|
|
35
|
+
model_cls = qs.model
|
|
36
|
+
manager = model_cls._default_manager
|
|
37
|
+
|
|
38
|
+
if hasattr(manager, 'simple') and self.action == 'list':
|
|
39
|
+
return manager.simple()
|
|
40
|
+
elif hasattr(manager, 'full'):
|
|
41
|
+
return manager.full()
|
|
42
|
+
|
|
20
43
|
|
|
21
|
-
|
|
22
|
-
# qs = super().get_queryset()
|
|
44
|
+
def get_serializer_class(self):
|
|
23
45
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
# def get_serializer_class(self):
|
|
36
|
-
# # Cambia el serializer según la acción
|
|
37
|
-
# if self.action == 'list' and self.simple_serializer_class:
|
|
38
|
-
# return self.simple_serializer_class
|
|
39
|
-
# if self.action in ['retrieve', 'update', 'partial_update'] and self.full_serializer_class:
|
|
40
|
-
# return self.full_serializer_class
|
|
41
|
-
# return super().get_serializer_class()
|
|
46
|
+
match self.action:
|
|
47
|
+
case 'create':
|
|
48
|
+
return self.write_serializer_class
|
|
49
|
+
case 'update' | 'partial_update':
|
|
50
|
+
return self.update_serializer_class
|
|
51
|
+
case 'list':
|
|
52
|
+
return self.simple_serializer_class
|
|
53
|
+
case 'retrieve':
|
|
54
|
+
return self.full_serializer_class
|
|
55
|
+
case _:
|
|
56
|
+
return super().get_serializer_class()
|
epok_toolkit/email/templates.py
CHANGED
|
@@ -1,29 +1,49 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
3
|
-
Catálogo central de plantillas de correo con estilo corporativo “morado minimalista”.
|
|
4
|
-
Cada plantilla declara las variables que necesita y se renderiza vía EmailTemplate.
|
|
2
|
+
|
|
5
3
|
"""
|
|
6
4
|
|
|
7
5
|
from dataclasses import dataclass
|
|
8
6
|
from typing import Dict, List
|
|
7
|
+
from django.conf import settings
|
|
8
|
+
import re
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# ============================
|
|
12
|
+
# Config base
|
|
13
|
+
# ============================
|
|
14
|
+
|
|
15
|
+
TEMPLATES_SETTINGS = getattr(settings, "TEMPLATE_SETTINGS", {})
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class Colors:
|
|
20
|
+
BACKGROUND: str = TEMPLATES_SETTINGS.get("colors", {}).get("background", "#f9fafb")
|
|
21
|
+
PRIMARY: str = TEMPLATES_SETTINGS.get("colors", {}).get("primary", "#4f46e5")
|
|
22
|
+
TEXT: str = TEMPLATES_SETTINGS.get("colors", {}).get("text", "#374151")
|
|
23
|
+
WHITE: str = TEMPLATES_SETTINGS.get("colors", {}).get("white", "#ffffff")
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class Company:
|
|
27
|
+
name: str = TEMPLATES_SETTINGS.get("company", {}).get("name", "Congrats")
|
|
28
|
+
email: str = TEMPLATES_SETTINGS.get("company", {}).get("email", "info@compania.com")
|
|
29
|
+
eslogan: str = TEMPLATES_SETTINGS.get("company", {}).get("eslogan", "Eslogan sin definir")
|
|
30
|
+
footer: str = TEMPLATES_SETTINGS.get("company", {}).get("footer", "¡Nos vemos pronto!<br><em>El equipo de Congrats 🥳</em>")
|
|
31
|
+
|
|
9
32
|
|
|
10
33
|
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
34
|
+
# ============================
|
|
35
|
+
# Grupper (envoltorio HTML)
|
|
36
|
+
# ============================
|
|
37
|
+
|
|
14
38
|
def wrap_html(content: str) -> str:
|
|
15
|
-
"""
|
|
16
|
-
Envuelve el bloque `content` en el layout HTML corporativo de Congrats.
|
|
17
|
-
"""
|
|
18
39
|
return f"""
|
|
19
|
-
<div style="font-family: Arial, Helvetica, sans-serif; background-color:
|
|
20
|
-
<table width="100%" cellpadding="0" cellspacing="0" style="max-width: 600px; margin: 0 auto; background:
|
|
40
|
+
<div style="font-family: Arial, Helvetica, sans-serif; background-color: {Colors.BACKGROUND}; padding: 24px;">
|
|
41
|
+
<table width="100%" cellpadding="0" cellspacing="0" style="max-width: 600px; margin: 0 auto; background: {Colors.WHITE}; border-radius: 8px; overflow: hidden;">
|
|
21
42
|
<tr>
|
|
22
|
-
<td style="background:
|
|
23
|
-
<h1 style="color:
|
|
43
|
+
<td style="background: {Colors.PRIMARY}; padding: 20px 24px; text-align: center;">
|
|
44
|
+
<h1 style="color: {Colors.WHITE}; margin: 0; font-size: 24px;">{Company.name} 🎉</h1>
|
|
24
45
|
</td>
|
|
25
46
|
</tr>
|
|
26
|
-
|
|
27
47
|
<tr>
|
|
28
48
|
<td style="padding: 32px 24px;">
|
|
29
49
|
{content}
|
|
@@ -34,24 +54,18 @@ def wrap_html(content: str) -> str:
|
|
|
34
54
|
"""
|
|
35
55
|
|
|
36
56
|
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
57
|
+
# ============================
|
|
58
|
+
# Tipos
|
|
59
|
+
# ============================
|
|
60
|
+
|
|
40
61
|
@dataclass(frozen=True)
|
|
41
62
|
class RenderedEmail:
|
|
42
63
|
subject: str
|
|
43
64
|
plain: str
|
|
44
65
|
html: str
|
|
45
66
|
|
|
46
|
-
|
|
47
67
|
@dataclass(frozen=True)
|
|
48
68
|
class EmailTemplate:
|
|
49
|
-
"""
|
|
50
|
-
subject – Cadena con placeholders, e.g. 'Hola {name}'
|
|
51
|
-
plain_body – Versión texto plano
|
|
52
|
-
html_body – HTML completo (usa wrap_html)
|
|
53
|
-
required_vars – Lista de variables obligatorias
|
|
54
|
-
"""
|
|
55
69
|
subject: str
|
|
56
70
|
plain_body: str
|
|
57
71
|
html_body: str
|
|
@@ -61,125 +75,99 @@ class EmailTemplate:
|
|
|
61
75
|
missing = [v for v in self.required_vars if v not in context]
|
|
62
76
|
if missing:
|
|
63
77
|
raise ValueError(f"Faltan llaves en contexto: {missing}")
|
|
64
|
-
|
|
65
78
|
return RenderedEmail(
|
|
66
79
|
subject=self.subject.format(**context),
|
|
67
80
|
plain=self.plain_body.format(**context),
|
|
68
81
|
html=self.html_body.format(**context),
|
|
69
82
|
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ============================
|
|
86
|
+
# Registro dinámico
|
|
87
|
+
# ============================
|
|
70
88
|
|
|
89
|
+
class TemplateRegistry:
|
|
90
|
+
def __init__(self):
|
|
91
|
+
self._templates: Dict[str, EmailTemplate] = {}
|
|
92
|
+
|
|
93
|
+
def _html_to_plain(self, html: str) -> str:
|
|
94
|
+
return re.sub(r"<[^>]*>", "", html).strip()
|
|
71
95
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
"<p style='text-align:center;margin:24px 0;'>"
|
|
90
|
-
"<a href='{reset_link}' style='display:inline-block;padding:12px 24px;font-size:16px;"
|
|
91
|
-
"color:#ffffff;background-color:#4f46e5;text-decoration:none;border-radius:5px;'>"
|
|
92
|
-
"🔒 Restablecer Contraseña 🎊</a>"
|
|
93
|
-
"</p>"
|
|
94
|
-
"<p style='font-size:16px;'>"
|
|
95
|
-
"Si eso no funciona, copia y pega este enlace en tu navegador:<br>"
|
|
96
|
-
"<span style='word-break:break-all;font-size:14px;'>{reset_link}</span>"
|
|
97
|
-
"</p>"
|
|
98
|
-
"<p style='font-size:16px;margin-top:24px;'>"
|
|
99
|
-
"Si no solicitaste esto, tranquilo, nada cambió. 😌<br><br>"
|
|
100
|
-
"¡A celebrar pronto!<br><em>El equipo de Congrats 🥳</em>"
|
|
101
|
-
"</p>"
|
|
102
|
-
),
|
|
103
|
-
required_vars=["name", "reset_link"],
|
|
104
|
-
),
|
|
96
|
+
def register_template(self, key: str, subject: str, html_body: str, required_vars: List[str], plain_body: str | None = None) -> None:
|
|
97
|
+
if key in self._templates:
|
|
98
|
+
raise ValueError(f"Template con clave '{key}' ya está registrado.")
|
|
99
|
+
plain = plain_body or self._html_to_plain(html_body)
|
|
100
|
+
wrapped_html = wrap_html(html_body)
|
|
101
|
+
self._templates[key] = EmailTemplate(
|
|
102
|
+
subject=subject,
|
|
103
|
+
plain_body=plain,
|
|
104
|
+
html_body=wrapped_html,
|
|
105
|
+
required_vars=required_vars
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def templates(self) -> Dict[str, EmailTemplate]:
|
|
110
|
+
return self._templates
|
|
111
|
+
|
|
112
|
+
registry = TemplateRegistry()
|
|
105
113
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
required_vars=["name"],
|
|
114
|
+
# ============================
|
|
115
|
+
# Registrar plantillas base usando el mismo builder
|
|
116
|
+
# ============================
|
|
117
|
+
|
|
118
|
+
registry.register_template(
|
|
119
|
+
key="password_reset",
|
|
120
|
+
subject=f"🔑 Restablecimiento de Contraseña – {Company.name}",
|
|
121
|
+
html_body=(
|
|
122
|
+
"<p style='font-size:16px;'>¡Hola <strong>{name}</strong>! 🎉</p>"
|
|
123
|
+
"<p style='font-size:16px;margin:24px 0;'>Haz clic en el botón para restablecer tu contraseña:</p>"
|
|
124
|
+
"<p style='text-align:center;margin:24px 0;'>"
|
|
125
|
+
f"<a href='{{reset_link}}' style='display:inline-block;padding:12px 24px;font-size:16px;color:{Colors.WHITE};background-color:{Colors.PRIMARY};text-decoration:none;border-radius:5px;'>🔒 Restablecer Contraseña 🎊</a>"
|
|
126
|
+
"</p>"
|
|
127
|
+
"<p style='font-size:16px;'>Si no funciona, copia este enlace:<br>"
|
|
128
|
+
"<span style='word-break:break-all;font-size:14px;'>{reset_link}</span></p>"
|
|
129
|
+
f"<p style='font-size:16px;margin-top:24px;'>{Company.footer}</p>"
|
|
123
130
|
),
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
html_body=wrap_html(
|
|
135
|
-
"<p style='font-size:16px;'>¡Hola <strong>{name}</strong>! 🔑</p>"
|
|
136
|
-
"<p style='font-size:16px;margin:24px 0;'>"
|
|
137
|
-
"Tu contraseña ha sido restablecida con éxito. Ahora vuelve a la pista de baile. 🎉"
|
|
138
|
-
"</p>"
|
|
139
|
-
"<p style='font-size:16px;margin-top:24px;'>"
|
|
140
|
-
"Si no fuiste tú, ignora o avísanos. 🤔<br><em>El equipo de Congrats 🥳</em>"
|
|
141
|
-
"</p>"
|
|
142
|
-
),
|
|
143
|
-
required_vars=["name"],
|
|
131
|
+
required_vars=["name", "reset_link"]
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
registry.register_template(
|
|
135
|
+
key="welcome",
|
|
136
|
+
subject=f"🎉 Bienvenido a {Company.name}, {{name}}",
|
|
137
|
+
html_body=(
|
|
138
|
+
"<p style='font-size:16px;'>¡Hola <strong>{name}</strong>! 🎈</p>"
|
|
139
|
+
"<p style='font-size:16px;margin:24px 0;'>Gracias por unirte a <strong>{Company.name}</strong>.</p>"
|
|
140
|
+
f"<p style='font-size:16px;margin-top:24px;'>{Company.footer}</p>"
|
|
144
141
|
),
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
"<p style='font-size:16px;'>¡Hola <strong>{name}</strong>! 🎟️</p>"
|
|
156
|
-
"<p style='font-size:16px;margin:24px 0;'>"
|
|
157
|
-
"Tus {tickets} tickets para <strong>{reunion_name}</strong> están listos. ¡Nos vemos en la fiesta! 🎊"
|
|
158
|
-
"</p>"
|
|
159
|
-
"<p style='font-size:16px;margin-top:24px;'>"
|
|
160
|
-
"¡Que lo disfrutes!<br><em>El equipo de Congrats 🥳</em>"
|
|
161
|
-
"</p>"
|
|
162
|
-
),
|
|
163
|
-
required_vars=["name", "reunion_name", "tickets"],
|
|
142
|
+
required_vars=["name"]
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
registry.register_template(
|
|
146
|
+
key="password_reset_success",
|
|
147
|
+
subject=f"🔑 Contraseña restablecida – {Company.name}",
|
|
148
|
+
html_body=(
|
|
149
|
+
"<p style='font-size:16px;'>¡Hola <strong>{name}</strong>! 🔑</p>"
|
|
150
|
+
"<p style='font-size:16px;margin:24px 0;'>Tu contraseña ha sido restablecida con éxito 🎉</p>"
|
|
151
|
+
f"<p style='font-size:16px;margin-top:24px;'>{Company.footer}</p>"
|
|
164
152
|
),
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
"<p style='font-size:16px;'>¡Hola <strong>{name}</strong>! 🚀</p>"
|
|
176
|
-
"<p style='font-size:16px;margin:24px 0;'>"
|
|
177
|
-
"La prueba de funcionamiento pasó. Tu app está lista para la fiesta. 🎉"
|
|
178
|
-
"</p>"
|
|
179
|
-
"<p style='font-size:16px;margin-top:24px;'>"
|
|
180
|
-
"Sigue brillando.<br><em>El equipo de Congrats 🥳</em>"
|
|
181
|
-
"</p>"
|
|
182
|
-
),
|
|
183
|
-
required_vars=["name"],
|
|
153
|
+
required_vars=["name"]
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
registry.register_template(
|
|
157
|
+
key="test_app_running",
|
|
158
|
+
subject=f"🚀 Test OK – {Company.name}",
|
|
159
|
+
html_body=(
|
|
160
|
+
"<p style='font-size:16px;'>¡Hola <strong>{name}</strong>! 🚀</p>"
|
|
161
|
+
"<p style='font-size:16px;margin:24px 0;'>La prueba de funcionamiento pasó. Tu app está lista para rockear. 🎉</p>"
|
|
162
|
+
f"<p style='font-size:16px;margin-top:24px;'>{Company.footer}</p>"
|
|
184
163
|
),
|
|
164
|
+
required_vars=["name"]
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# ============================
|
|
168
|
+
# Catálogo final
|
|
169
|
+
# ============================
|
|
170
|
+
|
|
171
|
+
TEMPLATES: Dict[str, EmailTemplate] = {
|
|
172
|
+
**registry.templates,
|
|
185
173
|
}
|
epok_toolkit/pdf/ticket_pdf.py
CHANGED
|
@@ -4,10 +4,8 @@ from datetime import date, datetime
|
|
|
4
4
|
from reportlab.pdfbase.ttfonts import TTFont
|
|
5
5
|
from reportlab.pdfgen import canvas
|
|
6
6
|
from dataclasses import dataclass
|
|
7
|
-
from datetime import datetime
|
|
8
7
|
from typing import Optional
|
|
9
8
|
from PIL import Image
|
|
10
|
-
import locale
|
|
11
9
|
from uuid import uuid4, UUID
|
|
12
10
|
import os
|
|
13
11
|
import qrcode
|
|
@@ -15,8 +13,6 @@ from io import BytesIO
|
|
|
15
13
|
import importlib.resources
|
|
16
14
|
|
|
17
15
|
|
|
18
|
-
# pip install reportlab qrcode
|
|
19
|
-
|
|
20
16
|
|
|
21
17
|
fuente_path = os.path.join(os.path.dirname(__file__), "fuentes", "Kollektif-Bold.ttf")
|
|
22
18
|
pdfmetrics.registerFont(TTFont("kollektif", fuente_path))
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: epok-toolkit
|
|
3
|
+
Version: 1.11.2
|
|
4
|
+
Summary: Una herramienta para la gestión de tareas y procesos en Django con Celery.
|
|
5
|
+
Author-email: Fernando Leon Franco <fernanlee2131@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Framework :: Django
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.12.9
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: Django>=5.0.2
|
|
15
|
+
Requires-Dist: djangorestframework>=3.16.0
|
|
16
|
+
Requires-Dist: celery[redis]>=5.5.3
|
|
17
|
+
Requires-Dist: django-celery-beat>=2.6.0
|
|
18
|
+
Requires-Dist: colorstreak>=0.1.0
|
|
19
|
+
Requires-Dist: PyJWT>=2.10.1
|
|
20
|
+
Requires-Dist: reportlab>=4.4.3
|
|
21
|
+
Requires-Dist: qrcode>=8.2
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# EPOK Toolkit 'v1.11.2'
|
|
27
|
+
|
|
28
|
+
EPOK Toolkit es una librería de utilidades para proyectos Django y Python, diseñada para facilitar tareas comunes como envío de emails, mensajería WhatsApp, generación de PDFs y manejo avanzado de caché en APIs.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
## ✉️ Email
|
|
34
|
+
Módulo para envío de correos electrónicos con plantillas HTML y texto plano, registro dinámico y configuración visual.
|
|
35
|
+
|
|
36
|
+
👉 [Documentación completa del módulo Email](epok_toolkit/email/README.md)
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 🟢 Django
|
|
41
|
+
Utilidades avanzadas para proyectos Django y DRF: caché, campos personalizados, managers, respuestas, viewsets y utilidades extra.
|
|
42
|
+
|
|
43
|
+
� [Documentación completa del módulo Django](epok_toolkit/django/README.md)
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 💬 Mensajería WhatsApp
|
|
48
|
+
Módulo para envío de mensajes y archivos por WhatsApp, con manejo de conexión y errores.
|
|
49
|
+
|
|
50
|
+
� [Documentación completa del módulo Messaging](epok_toolkit/messaging/README.md)
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 🧾 PDF / Ticket Generator
|
|
55
|
+
Módulo para generación de tickets PDF personalizados usando ReportLab y plantillas gráficas.
|
|
56
|
+
|
|
57
|
+
� [Documentación completa del módulo PDF](epok_toolkit/pdf/README.md)
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## ⚙️ Configuración
|
|
62
|
+
|
|
63
|
+
Toda la configuración se centraliza en `default_settings.py`, permitiendo personalizar colores, datos de empresa, y más.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 🚀 Instalación
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip install epok-toolkit
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
¿Quieres agregar ejemplos avanzados o documentación de otro módulo? ¡Solicítalo!
|
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
epok_toolkit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
epok_toolkit/apps.py,sha256=O3q3CcucJOHjlYIS0VgbKsbtim2hpng_FxpKEG_MlWs,486
|
|
3
|
-
epok_toolkit/default_settings.py,sha256=
|
|
3
|
+
epok_toolkit/default_settings.py,sha256=GdDpwMPzRYNDTDK0zcQKUwgiGYVVhj1Pi1NGAxTE1Io,890
|
|
4
4
|
epok_toolkit/django/__init__.py,sha256=k8Fs3tI_DAJPmhZ_fIcWwZWPog3KgYyg4-a2cJWwDYc,113
|
|
5
5
|
epok_toolkit/django/cache.py,sha256=R179_Hrlw1k0tsry9EN0zGl4-Of02L-VzE-xqFuXjaU,4107
|
|
6
6
|
epok_toolkit/django/fields.py,sha256=-ajP5qx-4bt9Qz9yW48gTlinTxD1xWPKOEkslqx8cSM,1089
|
|
7
7
|
epok_toolkit/django/manager.py,sha256=3MZcA9wQY4E1KD8XlgZQbzf4wlF9vA8Pntl_gKDWfpA,1350
|
|
8
8
|
epok_toolkit/django/response.py,sha256=O8OHBaKgUQjBeYLLbgTTs669l_4D6swUgAOwswjc-88,1716
|
|
9
|
-
epok_toolkit/django/viewsets.py,sha256=
|
|
9
|
+
epok_toolkit/django/viewsets.py,sha256=zoWiPcIkrlmmjz695XjiIPOtNx1RvOChLva7RnrahI0,1635
|
|
10
10
|
epok_toolkit/django/utils/__init__.py,sha256=zDuoqm_eksZQpL-Bvd_q2KGMtSXfprBjf4TScZiwV6k,25
|
|
11
11
|
epok_toolkit/django/utils/magic_link.py,sha256=GiDuy0kAGdYohGPlBL7rwpKPMpXuB1wJa1k5LTAOm4w,889
|
|
12
12
|
epok_toolkit/email/__init__.py,sha256=pyJwysyVoq6DuYAG72fulsKFoOuAfjw3aBH7FhmYGHc,35
|
|
13
13
|
epok_toolkit/email/email_async.py,sha256=oC0WowWNUpTpXdxo6hJag5gWUaStxI6VBEArEKQxXko,1521
|
|
14
14
|
epok_toolkit/email/engine.py,sha256=IIifqRI9z76pHdrO5oSSZ25aP5txOTAgrj1JuVVPlMY,2387
|
|
15
|
-
epok_toolkit/email/templates.py,sha256=
|
|
15
|
+
epok_toolkit/email/templates.py,sha256=8aQ5E3A0q1Uqc7WZf7Tm_ssj6DgAGNHiLsAd6mrZff4,6136
|
|
16
16
|
epok_toolkit/messaging/__init__.py,sha256=lo0URCOr8tR62ljc0MWTtYZ_YR-tecQaAhhAbyGcOxs,53
|
|
17
17
|
epok_toolkit/messaging/whatsapp.py,sha256=TrMSiKzvnhWOotSDEGil1BGgOJ7jLK7h3MXKdW3zCJw,14114
|
|
18
18
|
epok_toolkit/messaging/whatsapp_instanced.py,sha256=q1CR--9N7IsR5MSf6Fps_l0zpHzTBFcJnB5nw14a28U,1414
|
|
19
19
|
epok_toolkit/pdf/__init__.py,sha256=Scb1iOYnVIUEiUVHLNaPmcigyD-jOSBs3ws5RmolMKE,33
|
|
20
|
-
epok_toolkit/pdf/ticket_pdf.py,sha256=
|
|
20
|
+
epok_toolkit/pdf/ticket_pdf.py,sha256=jktxxjEL4gFfnvXZW7P7y35bllOe9sJ3S-eOkgPcEQw,8913
|
|
21
21
|
epok_toolkit/pdf/fuentes/Kollektif-Bold.ttf,sha256=MiaucCL_aPGhbDl6M0xA2g2nf84MXHGciOd-XSw0XRo,78780
|
|
22
22
|
epok_toolkit/pdf/fuentes/Kollektif-BoldItalic.ttf,sha256=hPMiWYQ0fLFfOKvV_OXFCwAaHnNMYzic_DPiqWPQL5w,71912
|
|
23
23
|
epok_toolkit/pdf/fuentes/Kollektif-Italic.ttf,sha256=1CXPyw43il9u0tQ_7aRzsEaVtg3x3_oJ1vuMMvEaDp8,42048
|
|
24
24
|
epok_toolkit/pdf/fuentes/Kollektif.ttf,sha256=7wTLkVVNUm1giLjIZcWRUH5r2r3o0GjdKic4V1A-pNQ,51128
|
|
25
25
|
epok_toolkit/pdf/plantillas/Ticket_congrats.png,sha256=OSQhVR0j_nLHE6kSJ33BTR-77HM1fNAfJBe2EuX6wVk,157141
|
|
26
26
|
epok_toolkit/pdf/plantillas/Ticket_congrats2.png,sha256=1RBogBdo-8WSMpD3H73HoLgJtr5EC5oVKfOCIWOxPSo,373605
|
|
27
|
-
epok_toolkit-1.11.
|
|
28
|
-
epok_toolkit-1.11.
|
|
29
|
-
epok_toolkit-1.11.
|
|
30
|
-
epok_toolkit-1.11.
|
|
31
|
-
epok_toolkit-1.11.
|
|
27
|
+
epok_toolkit-1.11.2.dist-info/licenses/LICENSE,sha256=iLDbGXdLSIOT5OsxzHCvtmxHtonE21GiFlS3LNkug4A,128
|
|
28
|
+
epok_toolkit-1.11.2.dist-info/METADATA,sha256=p1bniBsDkpw5eo9VrBM3iVn73fZkkNGgdH0Onu0dJPQ,2217
|
|
29
|
+
epok_toolkit-1.11.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
30
|
+
epok_toolkit-1.11.2.dist-info/top_level.txt,sha256=Wo72AqIFcfWwBGM5F5iGFw9PrO3WBnTSprFZIJk_pNg,13
|
|
31
|
+
epok_toolkit-1.11.2.dist-info/RECORD,,
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: epok-toolkit
|
|
3
|
-
Version: 1.11.1
|
|
4
|
-
Summary: Una herramienta para la gestión de tareas y procesos en Django con Celery.
|
|
5
|
-
Author-email: Fernando Leon Franco <fernanlee2131@gmail.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Classifier: Programming Language :: Python :: 3
|
|
8
|
-
Classifier: Framework :: Django
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
-
Classifier: Operating System :: OS Independent
|
|
11
|
-
Requires-Python: >=3.12.9
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
License-File: LICENSE
|
|
14
|
-
Requires-Dist: Django>=5.0.2
|
|
15
|
-
Requires-Dist: djangorestframework>=3.16.0
|
|
16
|
-
Requires-Dist: celery[redis]>=5.5.3
|
|
17
|
-
Requires-Dist: django-celery-beat>=2.6.0
|
|
18
|
-
Requires-Dist: colorstreak>=0.1.0
|
|
19
|
-
Requires-Dist: PyJWT>=2.10.1
|
|
20
|
-
Dynamic: license-file
|
|
21
|
-
|
|
22
|
-
# EPOK Toolkit
|
|
23
|
-
|
|
24
|
-
Esta libreria contiene utilidades
|
|
25
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|