dmart 0.1.4__py3-none-any.whl → 0.1.8__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.
Files changed (49) hide show
  1. alembic/scripts/__init__.py +0 -0
  2. alembic/scripts/calculate_checksums.py +77 -0
  3. alembic/scripts/migration_f7a4949eed19.py +28 -0
  4. alembic/versions/0f3d2b1a7c21_add_authz_materialized_views.py +87 -0
  5. alembic/versions/10d2041b94d4_last_checksum_history.py +62 -0
  6. alembic/versions/1cf4e1ee3cb8_ext_permission_with_filter_fields_values.py +33 -0
  7. alembic/versions/26bfe19b49d4_rm_failedloginattempts.py +42 -0
  8. alembic/versions/3c8bca2219cc_add_otp_table.py +38 -0
  9. alembic/versions/6675fd9dfe42_remove_unique_from_sessions_table.py +36 -0
  10. alembic/versions/71bc1df82e6a_adding_user_last_login_at.py +43 -0
  11. alembic/versions/74288ccbd3b5_initial.py +264 -0
  12. alembic/versions/7520a89a8467_rm_activesession_table.py +39 -0
  13. alembic/versions/848b623755a4_make_created_nd_updated_at_required.py +138 -0
  14. alembic/versions/8640dcbebf85_add_notes_to_users.py +32 -0
  15. alembic/versions/91c94250232a_adding_fk_on_owner_shortname.py +104 -0
  16. alembic/versions/98ecd6f56f9a_ext_meta_with_owner_group_shortname.py +66 -0
  17. alembic/versions/9aae9138c4ef_indexing_created_at_updated_at.py +80 -0
  18. alembic/versions/__init__.py +0 -0
  19. alembic/versions/b53f916b3f6d_json_to_jsonb.py +492 -0
  20. alembic/versions/eb5f1ec65156_adding_user_locked_to_device.py +36 -0
  21. alembic/versions/f7a4949eed19_adding_query_policies_to_meta.py +60 -0
  22. api/user/model/__init__.py +0 -0
  23. api/user/model/errors.py +14 -0
  24. api/user/model/requests.py +165 -0
  25. api/user/model/responses.py +11 -0
  26. dmart-0.1.8.dist-info/METADATA +64 -0
  27. {dmart-0.1.4.dist-info → dmart-0.1.8.dist-info}/RECORD +48 -5
  28. plugins/action_log/__init__.py +0 -0
  29. plugins/action_log/plugin.py +121 -0
  30. plugins/admin_notification_sender/__init__.py +0 -0
  31. plugins/admin_notification_sender/plugin.py +124 -0
  32. plugins/ldap_manager/__init__.py +0 -0
  33. plugins/ldap_manager/plugin.py +100 -0
  34. plugins/local_notification/__init__.py +0 -0
  35. plugins/local_notification/plugin.py +123 -0
  36. plugins/realtime_updates_notifier/__init__.py +0 -0
  37. plugins/realtime_updates_notifier/plugin.py +58 -0
  38. plugins/redis_db_update/__init__.py +0 -0
  39. plugins/redis_db_update/plugin.py +188 -0
  40. plugins/resource_folders_creation/__init__.py +0 -0
  41. plugins/resource_folders_creation/plugin.py +81 -0
  42. plugins/system_notification_sender/__init__.py +0 -0
  43. plugins/system_notification_sender/plugin.py +188 -0
  44. plugins/update_access_controls/__init__.py +0 -0
  45. plugins/update_access_controls/plugin.py +9 -0
  46. dmart-0.1.4.dist-info/METADATA +0 -9
  47. {dmart-0.1.4.dist-info → dmart-0.1.8.dist-info}/WHEEL +0 -0
  48. {dmart-0.1.4.dist-info → dmart-0.1.8.dist-info}/entry_points.txt +0 -0
  49. {dmart-0.1.4.dist-info → dmart-0.1.8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,188 @@
1
+ from sys import modules as sys_modules
2
+ from uuid import uuid4
3
+ from models import api
4
+ from models.enums import ContentType, QueryType
5
+ from models.core import (
6
+ ActionType,
7
+ Content,
8
+ NotificationData,
9
+ Payload,
10
+ PluginBase,
11
+ Event,
12
+ Translation,
13
+ )
14
+ from utils.notification import NotificationManager
15
+ from utils.helpers import camel_case, replace_message_vars
16
+ from utils.settings import settings
17
+ from fastapi.logger import logger
18
+ from data_adapters.adapter import data_adapter as db
19
+
20
+
21
+ class Plugin(PluginBase):
22
+ async def hook(self, data: Event):
23
+ """
24
+ after any action
25
+ 1- get the matching SystemNotificationRequest for this action
26
+ 2- generate list of users to send the notification to (based on the action entry):
27
+ 2.1- entry.owner_shortname, entry.group.members?, and entry.collaborators?
28
+ 3- send the notification to the list of generated users
29
+ """
30
+ # Type narrowing for PyRight
31
+ if not isinstance(data.shortname, str):
32
+ logger.warning(
33
+ "data.shortname is None and str is required at system_notification_sender"
34
+ )
35
+ return
36
+
37
+ if data.action_type == ActionType.delete and data.attributes.get("entry"):
38
+ entry = data.attributes["entry"].model_dump()
39
+ else:
40
+ entry = (
41
+ await db.load(
42
+ data.space_name,
43
+ data.subpath,
44
+ data.shortname,
45
+ getattr(sys_modules["models.core"], camel_case(data.resource_type)),
46
+ data.user_shortname,
47
+ )
48
+ ).model_dump()
49
+ if entry["payload"] is not None:
50
+ try:
51
+ entry["payload"]["body"] = await db.load_resource_payload(
52
+ space_name=data.space_name,
53
+ subpath=data.subpath,
54
+ filename=entry["payload"]["body"],
55
+ class_type=getattr(
56
+ sys_modules["models.core"], camel_case(data.resource_type)
57
+ ),
58
+ )
59
+ except Exception as e:
60
+ logger.warning(
61
+ f"Failed to load payload for entry {data.space_name}/{data.subpath}/{data.shortname}: {e}"
62
+ )
63
+ entry["space_name"] = data.space_name
64
+ entry["resource_type"] = str(data.resource_type)
65
+ entry["subpath"] = data.subpath
66
+ # 1- get the matching SystemNotificationRequests
67
+ total, matching_notification_requests = await db.query(api.Query(
68
+ type=QueryType.search,
69
+ retrieve_json_payload=True,
70
+ space_name="management",
71
+ subpath="notifications/system",
72
+ search=f"@payload.body.on_space:{data.space_name} @payload.body.on_subpath:{data.subpath.lstrip("/")} @payload.body.on_action:{data.action_type}",
73
+ limit=30,
74
+ offset=0
75
+ ), "dmart")
76
+ if total == 0:
77
+ return
78
+
79
+ sub_matching_notification_requests = matching_notification_requests[0].model_dump()
80
+ notification_dict = sub_matching_notification_requests
81
+ if (
82
+ "state" in entry
83
+ and notification_dict.get("on_state", "") != ""
84
+ and notification_dict["on_state"] != entry["state"]
85
+ ):
86
+ return
87
+
88
+
89
+ # 2- get list of subscribed users
90
+ notification_subscribers =[]
91
+ # if entry.get("collaborators", None):
92
+ # notification_subscribers.extend(entry["collaborators"].values())
93
+
94
+ data_owner_shortname = entry["owner_shortname"]
95
+ if entry.get("owner_group_shortname", None):
96
+ group_users = await db.get_group_users(entry["owner_group_shortname"])
97
+ notification_subscribers.extend(group_users)
98
+ if data_owner_shortname in notification_subscribers:
99
+ notification_subscribers.remove(data_owner_shortname)
100
+ users_objects: dict[str, dict] = {}
101
+
102
+ for subscriber in notification_subscribers:
103
+ users_objects[subscriber] = (await db.load(
104
+ settings.management_space,
105
+ settings.users_subpath,
106
+ subscriber,
107
+ getattr(sys_modules["models.core"], camel_case("user")),
108
+ data.user_shortname,
109
+ )).model_dump()
110
+ # 3- send the notification
111
+ notification_manager = NotificationManager()
112
+
113
+ formatted_req = await self.prepare_request(notification_dict, entry)
114
+ for receiver in set(notification_subscribers):
115
+ if not formatted_req["push_only"]:
116
+ notification_content = Content(
117
+ shortname=str(uuid4())[:8],
118
+ is_active=True,
119
+ displayname=notification_dict["attributes"]["displayname"],
120
+ description=notification_dict["attributes"]["description"],
121
+ owner_shortname=receiver,
122
+ payload=Payload(
123
+ content_type=ContentType.json,
124
+ body={
125
+ "type": "system",
126
+ "is_read": False,
127
+ "priority": notification_dict["attributes"]["payload"]["body"]["priority"],
128
+ "entry_space": entry["space_name"],
129
+ "entry_subpath": entry["subpath"],
130
+ "entry_shortname": entry["shortname"],
131
+ "resource_type": entry["resource_type"],
132
+ "created_by": data.user_shortname,
133
+ "action_type": str(data.action_type)
134
+ }
135
+ )
136
+ )
137
+ await db.internal_save_model(
138
+ space_name="personal",
139
+ subpath=f"people/{receiver}/notifications",
140
+ meta=notification_content
141
+ )
142
+
143
+ for platform in formatted_req["platforms"]:
144
+ await notification_manager.send(
145
+ platform=platform,
146
+ data=NotificationData(
147
+ receiver=users_objects[receiver],
148
+ title=formatted_req["title"],
149
+ body=formatted_req["body"],
150
+ image_urls=formatted_req["images_urls"],
151
+ deep_link=notification_dict.get("deep_link", {}),
152
+ entry_id=entry["shortname"],
153
+ ),
154
+ )
155
+
156
+ async def prepare_request(self, notification_dict: dict, entry: dict) -> dict:
157
+ for locale in ["ar", "en", "ku"]:
158
+ if "displayname" in notification_dict:
159
+ notification_dict["displayname"][locale] = replace_message_vars(
160
+ notification_dict["displayname"][locale], entry, locale
161
+ )
162
+ if "description" in notification_dict:
163
+ notification_dict["description"][locale] = replace_message_vars(
164
+ notification_dict["description"][locale], entry, locale
165
+ )
166
+
167
+ # Get Notification Request Images
168
+ attachments_path = (
169
+ settings.spaces_folder
170
+ / f"{settings.management_space}"
171
+ f"/{notification_dict['subpath']}/.dm/{notification_dict['shortname']}"
172
+ )
173
+ notification_attachments = await db.get_entry_attachments(
174
+ subpath=f"{notification_dict['subpath']}/{notification_dict['shortname']}",
175
+ attachments_path=attachments_path,
176
+ )
177
+ notification_images = {
178
+ "en": notification_attachments.get("media", {}).get("en"),
179
+ "ar": notification_attachments.get("media", {}).get("ar"),
180
+ "ku": notification_attachments.get("media", {}).get("ku"),
181
+ }
182
+ return {
183
+ "platforms": notification_dict.get("types", []),
184
+ "title": Translation(**notification_dict.get("displayname", {})),
185
+ "body": Translation(**notification_dict.get("description", {})),
186
+ "images_urls": Translation(**notification_images),
187
+ "push_only": notification_dict.get("push_only", False),
188
+ }
File without changes
@@ -0,0 +1,9 @@
1
+ from models.core import PluginBase, Event
2
+ from utils.access_control import access_control
3
+ from utils.settings import settings
4
+
5
+
6
+ class Plugin(PluginBase):
7
+ async def hook(self, data: Event):
8
+ if settings.active_data_db == "file":
9
+ await access_control.load_permissions_and_roles()
@@ -1,9 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: dmart
3
- Version: 0.1.4
4
- Requires-Python: >=3.10
5
- Provides-Extra: extra
6
- Provides-Extra: plugins
7
- Provides-Extra: all
8
- Dynamic: provides-extra
9
- Dynamic: requires-python
File without changes