vintasend-sqlalchemy 0.1.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.
- vintasend_sqlalchemy/__init__.py +0 -0
- vintasend_sqlalchemy/alembic_initial_migration_ops.py +34 -0
- vintasend_sqlalchemy/model_factory.py +115 -0
- vintasend_sqlalchemy/py.typed +0 -0
- vintasend_sqlalchemy/services/__init__.py +0 -0
- vintasend_sqlalchemy/services/notification_backends/__init__.py +0 -0
- vintasend_sqlalchemy/services/notification_backends/sqlalchemy_notification_backend.py +771 -0
- vintasend_sqlalchemy/services/tests/__init__.py +0 -0
- vintasend_sqlalchemy/services/tests/test_sqlalchemy_asyncio_notification_backend.py +525 -0
- vintasend_sqlalchemy/services/tests/test_sqlalchemy_notification_backend.py +412 -0
- vintasend_sqlalchemy-0.1.0.dist-info/LICENSE.txt +21 -0
- vintasend_sqlalchemy-0.1.0.dist-info/METADATA +23 -0
- vintasend_sqlalchemy-0.1.0.dist-info/RECORD +14 -0
- vintasend_sqlalchemy-0.1.0.dist-info/WHEEL +4 -0
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
|
|
3
|
+
import sqlalchemy as sa
|
|
4
|
+
from alembic import op
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def create_notification_table(user_id_type: type):
|
|
8
|
+
return op.create_table('notifications',
|
|
9
|
+
sa.Column("id", sa.BigInteger().with_variant(sa.Integer, "sqlite"), primary_key=True),
|
|
10
|
+
sa.Column('notification_type', sa.String(50), nullable=False),
|
|
11
|
+
sa.Column('title', sa.String(255), nullable=False),
|
|
12
|
+
sa.Column('status', sa.String(50), nullable=False, default="PENDING_SEND"),
|
|
13
|
+
sa.Column('body_template', sa.String(255), nullable=False),
|
|
14
|
+
sa.Column(
|
|
15
|
+
'created', sa.DateTime, default=lambda: datetime.datetime.now(datetime.timezone.utc)
|
|
16
|
+
),
|
|
17
|
+
sa.Column(
|
|
18
|
+
'updated',
|
|
19
|
+
sa.DateTime,
|
|
20
|
+
default=lambda: datetime.datetime.now(datetime.timezone.utc),
|
|
21
|
+
onupdate=lambda: datetime.datetime.now(datetime.timezone.utc),
|
|
22
|
+
),
|
|
23
|
+
sa.Column('subject_template', sa.String(255), nullable=True, default=""),
|
|
24
|
+
sa.Column('preheader_template', sa.String(255), nullable=True, default=""),
|
|
25
|
+
sa.Column('context_name', sa.String(255), nullable=True, default=""),
|
|
26
|
+
sa.Column('context_kwargs', sa.JSON, default=dict),
|
|
27
|
+
sa.Column('context_used', sa.JSON, nullable=True),
|
|
28
|
+
sa.Column('adapter_used', sa.String(255), nullable=True),
|
|
29
|
+
sa.Column('adapter_extra_parameters', sa.JSON, nullable=True),
|
|
30
|
+
sa.Column('send_after', sa.DateTime(), nullable=True),
|
|
31
|
+
sa.Column('user_id', user_id_type(), nullable=False),
|
|
32
|
+
sa.PrimaryKeyConstraint('id'),
|
|
33
|
+
sa.ForeignKeyConstraint(['user_id'], ['users.id'])
|
|
34
|
+
)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import uuid
|
|
3
|
+
from typing import Any, Generic, TypeVar
|
|
4
|
+
|
|
5
|
+
from sqlalchemy import JSON, BigInteger, DateTime, ForeignKey, Integer, String, null
|
|
6
|
+
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
|
|
7
|
+
from sqlalchemy.orm.decl_api import DeclarativeAttributeIntercept
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Base(DeclarativeBase):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class NotificationMixin(Base):
|
|
15
|
+
__abstract__ = True
|
|
16
|
+
id: Mapped[int] = mapped_column("id", BigInteger().with_variant(Integer, "sqlite"), primary_key=True) # noqa: A003
|
|
17
|
+
notification_type: Mapped[str] = mapped_column("notification_type", String(50), nullable=False)
|
|
18
|
+
title: Mapped[str] = mapped_column("title", String(255), nullable=False)
|
|
19
|
+
status: Mapped[str] = mapped_column("status", String(50), nullable=False, default="PENDING_SEND")
|
|
20
|
+
body_template: Mapped[str] = mapped_column("body_template", String(255), nullable=False)
|
|
21
|
+
created: Mapped[datetime.datetime] = mapped_column(
|
|
22
|
+
"created", DateTime, default=lambda: datetime.datetime.now(datetime.timezone.utc)
|
|
23
|
+
)
|
|
24
|
+
updated: Mapped[datetime.datetime] = mapped_column(
|
|
25
|
+
"updated",
|
|
26
|
+
DateTime,
|
|
27
|
+
default=lambda: datetime.datetime.now(datetime.timezone.utc),
|
|
28
|
+
onupdate=lambda: datetime.datetime.now(datetime.timezone.utc),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Email specific fields
|
|
32
|
+
subject_template: Mapped[str] = mapped_column("subject_template", String(255), nullable=True, default="")
|
|
33
|
+
preheader_template: Mapped[str] = mapped_column("preheader_template", String(255), nullable=True, default="")
|
|
34
|
+
context_name: Mapped[str] = mapped_column("context_name", String(255), nullable=True, default="")
|
|
35
|
+
context_kwargs: Mapped[dict] = mapped_column("context_kwargs", JSON, default=dict)
|
|
36
|
+
adapter_used: Mapped[str] = mapped_column("adapter_used", String(255), nullable=True)
|
|
37
|
+
context_used: Mapped[dict | None] = mapped_column("context_used", JSON, nullable=True)
|
|
38
|
+
adapter_extra_parameters: Mapped[dict | None] = mapped_column("adapter_extra_parameters", JSON, nullable=True)
|
|
39
|
+
|
|
40
|
+
send_after = mapped_column("send_after", DateTime, nullable=True)
|
|
41
|
+
|
|
42
|
+
def __str__(self):
|
|
43
|
+
return f"{self.get_user()} - {self.notification_type} - {self.title} - {self.status}{f' (scheduled to {self.send_after})' if self.send_after else ''}"
|
|
44
|
+
|
|
45
|
+
def get_user(self) -> Any:
|
|
46
|
+
raise NotImplementedError
|
|
47
|
+
|
|
48
|
+
def get_user_id(self) -> Any:
|
|
49
|
+
raise NotImplementedError
|
|
50
|
+
|
|
51
|
+
def get_user_email(self) -> str:
|
|
52
|
+
raise NotImplementedError
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def get_user_id_attr_name() -> str:
|
|
56
|
+
raise NotImplementedError
|
|
57
|
+
|
|
58
|
+
@staticmethod
|
|
59
|
+
def get_user_attr_name() -> str:
|
|
60
|
+
raise NotImplementedError
|
|
61
|
+
|
|
62
|
+
def set_user_id(self, user_id: Any):
|
|
63
|
+
raise NotImplementedError
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
UserType = TypeVar('UserType', bound=DeclarativeBase)
|
|
67
|
+
UserPrimaryKeyType = TypeVar('UserPrimaryKeyType', int, str, uuid.UUID)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class NotificationMeta(DeclarativeAttributeIntercept):
|
|
71
|
+
def __new__(cls, name, bases, dct, user_model, user_primary_key_field_name, user_primary_key_field_type):
|
|
72
|
+
if user_primary_key_field_type == int:
|
|
73
|
+
dct['user_id'] = mapped_column(ForeignKey(getattr(user_model, user_primary_key_field_name)))
|
|
74
|
+
dct['set_user_id'] = lambda self, user_id: setattr(self, 'user_id', user_id)
|
|
75
|
+
elif user_primary_key_field_type == str:
|
|
76
|
+
dct['user_id'] = mapped_column(ForeignKey(getattr(user_model, user_primary_key_field_name)))
|
|
77
|
+
dct['set_user_id'] = lambda self, user_id: setattr(self, 'user_id', user_id)
|
|
78
|
+
elif user_primary_key_field_type == uuid.UUID:
|
|
79
|
+
dct['user_id'] = mapped_column(ForeignKey(getattr(user_model, user_primary_key_field_name)))
|
|
80
|
+
dct['set_user_id'] = lambda self, user_id: setattr(self, 'user_id', user_id)
|
|
81
|
+
|
|
82
|
+
dct['user'] = relationship(user_model, backref="notifications")
|
|
83
|
+
dct['get_user_id'] = lambda self: self.user_id
|
|
84
|
+
dct['get_user'] = lambda self: self.user
|
|
85
|
+
dct['__tablename__'] = "notifications"
|
|
86
|
+
dct['__tableargs__'] = {"extend_existing": True}
|
|
87
|
+
|
|
88
|
+
return super().__new__(cls, name, bases, dct)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class GenericNotification(
|
|
92
|
+
NotificationMixin,
|
|
93
|
+
Generic[UserType, UserPrimaryKeyType],
|
|
94
|
+
):
|
|
95
|
+
__abstract__ = True
|
|
96
|
+
|
|
97
|
+
user: Mapped[UserType]
|
|
98
|
+
user_id: Mapped[UserPrimaryKeyType]
|
|
99
|
+
|
|
100
|
+
def get_user_id(self) -> UserPrimaryKeyType:
|
|
101
|
+
raise NotImplementedError
|
|
102
|
+
|
|
103
|
+
def set_user_id(self, user_id: UserPrimaryKeyType) -> None:
|
|
104
|
+
raise NotImplementedError
|
|
105
|
+
|
|
106
|
+
def get_user(self) -> UserType:
|
|
107
|
+
raise NotImplementedError
|
|
108
|
+
|
|
109
|
+
@staticmethod
|
|
110
|
+
def get_user_id_attr_name() -> str:
|
|
111
|
+
return "user_id"
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def get_user_attr_name() -> str:
|
|
115
|
+
return "user"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|