dmart 1.4.40.post6__py3-none-any.whl → 1.4.40.post7__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 (207) hide show
  1. dmart/sample/spaces/applications/.dm/meta.space.json +30 -0
  2. dmart/sample/spaces/applications/api/.dm/meta.folder.json +1 -0
  3. dmart/sample/spaces/applications/api/.dm/query_all_applications/meta.content.json +1 -0
  4. dmart/sample/spaces/applications/api/.dm/test_by_saad/attachments.media/meta.warframe.json +1 -0
  5. dmart/sample/spaces/applications/api/.dm/test_by_saad/attachments.media/warframe.png +0 -0
  6. dmart/sample/spaces/applications/api/.dm/test_by_saad/meta.content.json +1 -0
  7. dmart/sample/spaces/applications/api/.dm/user_profile/meta.content.json +1 -0
  8. dmart/sample/spaces/applications/api/applications/.dm/create_log/meta.content.json +1 -0
  9. dmart/sample/spaces/applications/api/applications/.dm/create_public_logs/meta.content.json +1 -0
  10. dmart/sample/spaces/applications/api/applications/.dm/meta.folder.json +1 -0
  11. dmart/sample/spaces/applications/api/applications/.dm/query_all_translated_data/meta.content.json +1 -0
  12. dmart/sample/spaces/applications/api/applications/.dm/query_logs/meta.content.json +1 -0
  13. dmart/sample/spaces/applications/api/applications/.dm/query_translated_enums/meta.content.json +1 -0
  14. dmart/sample/spaces/applications/api/applications/.dm/query_translated_others/meta.content.json +1 -0
  15. dmart/sample/spaces/applications/api/applications/.dm/query_translated_resolution/meta.content.json +1 -0
  16. dmart/sample/spaces/applications/api/applications/create_log.json +1 -0
  17. dmart/sample/spaces/applications/api/applications/create_public_logs.json +1 -0
  18. dmart/sample/spaces/applications/api/applications/query_all_translated_data.json +1 -0
  19. dmart/sample/spaces/applications/api/applications/query_logs.json +1 -0
  20. dmart/sample/spaces/applications/api/applications/query_translated_enums.json +1 -0
  21. dmart/sample/spaces/applications/api/applications/query_translated_others.json +1 -0
  22. dmart/sample/spaces/applications/api/applications/query_translated_resolution.json +1 -0
  23. dmart/sample/spaces/applications/api/applications.json +1 -0
  24. dmart/sample/spaces/applications/api/management/.dm/create_subaccount/meta.content.json +1 -0
  25. dmart/sample/spaces/applications/api/management/.dm/meta.folder.json +1 -0
  26. dmart/sample/spaces/applications/api/management/.dm/update_password/meta.content.json +1 -0
  27. dmart/sample/spaces/applications/api/management/create_subaccount.json +53 -0
  28. dmart/sample/spaces/applications/api/management/update_password.json +1 -0
  29. dmart/sample/spaces/applications/api/management.json +1 -0
  30. dmart/sample/spaces/applications/api/query_all_applications.json +15 -0
  31. dmart/sample/spaces/applications/api/test_by_saad.json +1 -0
  32. dmart/sample/spaces/applications/api/user/.dm/meta.folder.json +1 -0
  33. dmart/sample/spaces/applications/api/user/.dm/test_by_saad/meta.content.json +1 -0
  34. dmart/sample/spaces/applications/api/user/.dm/user_profile/meta.content.json +1 -0
  35. dmart/sample/spaces/applications/api/user/test_by_saad.json +1 -0
  36. dmart/sample/spaces/applications/api/user/user_profile.json +1 -0
  37. dmart/sample/spaces/applications/api/user_profile.json +1 -0
  38. dmart/sample/spaces/applications/api.json +1 -0
  39. dmart/sample/spaces/applications/collections/.dm/meta.folder.json +19 -0
  40. dmart/sample/spaces/applications/collections.json +1 -0
  41. dmart/sample/spaces/applications/configurations/.dm/meta.folder.json +1 -0
  42. dmart/sample/spaces/applications/configurations/time_out.json +1 -0
  43. dmart/sample/spaces/applications/configurations.json +19 -0
  44. dmart/sample/spaces/applications/errors.json +1 -0
  45. dmart/sample/spaces/applications/logs/.dm/meta.folder.json +1 -0
  46. dmart/sample/spaces/applications/logs.json +1 -0
  47. dmart/sample/spaces/applications/queries/.dm/meta.folder.json +1 -0
  48. dmart/sample/spaces/applications/queries/.dm/order/meta.content.json +1 -0
  49. dmart/sample/spaces/applications/queries/order.json +1 -0
  50. dmart/sample/spaces/applications/queries.json +1 -0
  51. dmart/sample/spaces/applications/schema/.dm/api/meta.schema.json +1 -0
  52. dmart/sample/spaces/applications/schema/.dm/configuration/meta.schema.json +1 -0
  53. dmart/sample/spaces/applications/schema/.dm/error/meta.schema.json +1 -0
  54. dmart/sample/spaces/applications/schema/.dm/log/meta.schema.json +1 -0
  55. dmart/sample/spaces/applications/schema/.dm/meta.folder.json +1 -0
  56. dmart/sample/spaces/applications/schema/.dm/query/meta.schema.json +16 -0
  57. dmart/sample/spaces/applications/schema/.dm/translation/meta.schema.json +1 -0
  58. dmart/sample/spaces/applications/schema/api.json +28 -0
  59. dmart/sample/spaces/applications/schema/configuration.json +1 -0
  60. dmart/sample/spaces/applications/schema/error.json +43 -0
  61. dmart/sample/spaces/applications/schema/log.json +1 -0
  62. dmart/sample/spaces/applications/schema/query.json +118 -0
  63. dmart/sample/spaces/applications/schema/translation.json +26 -0
  64. dmart/sample/spaces/applications/schema.json +1 -0
  65. dmart/sample/spaces/applications/translations/.dm/meta.folder.json +1 -0
  66. dmart/sample/spaces/applications/translations.json +1 -0
  67. dmart/sample/spaces/archive/.dm/meta.space.json +27 -0
  68. dmart/sample/spaces/custom_plugins/dummy/__pycache__/plugin.cpython-314.pyc +0 -0
  69. dmart/sample/spaces/custom_plugins/dummy/config.json +28 -0
  70. dmart/sample/spaces/custom_plugins/dummy/plugin.py +6 -0
  71. dmart/sample/spaces/custom_plugins/missed_entry/config.json +12 -0
  72. dmart/sample/spaces/custom_plugins/missed_entry/plugin.py +119 -0
  73. dmart/sample/spaces/custom_plugins/own_changed_notification/__pycache__/plugin.cpython-314.pyc +0 -0
  74. dmart/sample/spaces/custom_plugins/own_changed_notification/config.json +12 -0
  75. dmart/sample/spaces/custom_plugins/own_changed_notification/plugin.py +65 -0
  76. dmart/sample/spaces/custom_plugins/reports_stats/config.json +14 -0
  77. dmart/sample/spaces/custom_plugins/reports_stats/plugin.py +82 -0
  78. dmart/sample/spaces/custom_plugins/system_notification_sender/config.json +22 -0
  79. dmart/sample/spaces/custom_plugins/system_notification_sender/notification.py +268 -0
  80. dmart/sample/spaces/custom_plugins/system_notification_sender/plugin.py +98 -0
  81. dmart/sample/spaces/management/.dm/events.jsonl +32 -0
  82. dmart/sample/spaces/management/.dm/meta.space.json +48 -0
  83. dmart/sample/spaces/management/.dm/notifications/attachments.view.json/admin.json +36 -0
  84. dmart/sample/spaces/management/.dm/notifications/attachments.view.json/meta.admin.json +1 -0
  85. dmart/sample/spaces/management/.dm/notifications/attachments.view.json/meta.system.json +1 -0
  86. dmart/sample/spaces/management/.dm/notifications/attachments.view.json/system.json +32 -0
  87. dmart/sample/spaces/management/collections/.dm/meta.folder.json +1 -0
  88. dmart/sample/spaces/management/collections.json +1 -0
  89. dmart/sample/spaces/management/groups/.dm/meta.folder.json +1 -0
  90. dmart/sample/spaces/management/groups.json +1 -0
  91. dmart/sample/spaces/management/health_check/.dm/meta.folder.json +1 -0
  92. dmart/sample/spaces/management/health_check.json +1 -0
  93. dmart/sample/spaces/management/notifications/.dm/meta.folder.json +1 -0
  94. dmart/sample/spaces/management/notifications/admin/.dm/meta.folder.json +9 -0
  95. dmart/sample/spaces/management/notifications/system/.dm/meta.folder.json +9 -0
  96. dmart/sample/spaces/management/notifications.json +1 -0
  97. dmart/sample/spaces/management/permissions/.dm/access_applications/meta.permission.json +31 -0
  98. dmart/sample/spaces/management/permissions/.dm/access_applications_world/meta.permission.json +31 -0
  99. dmart/sample/spaces/management/permissions/.dm/access_messages/meta.permission.json +23 -0
  100. dmart/sample/spaces/management/permissions/.dm/access_personal/meta.permission.json +40 -0
  101. dmart/sample/spaces/management/permissions/.dm/access_protected/meta.permission.json +33 -0
  102. dmart/sample/spaces/management/permissions/.dm/access_public/meta.permission.json +24 -0
  103. dmart/sample/spaces/management/permissions/.dm/browse_all_folders/meta.permission.json +23 -0
  104. dmart/sample/spaces/management/permissions/.dm/create_log/meta.permission.json +24 -0
  105. dmart/sample/spaces/management/permissions/.dm/interviewer/meta.permission.json +1 -0
  106. dmart/sample/spaces/management/permissions/.dm/manage_applications/meta.permission.json +1 -0
  107. dmart/sample/spaces/management/permissions/.dm/manage_debug/meta.permission.json +25 -0
  108. dmart/sample/spaces/management/permissions/.dm/manage_spaces/meta.permission.json +24 -0
  109. dmart/sample/spaces/management/permissions/.dm/meta.folder.json +1 -0
  110. dmart/sample/spaces/management/permissions/.dm/rules_management_default/meta.permission.json +32 -0
  111. dmart/sample/spaces/management/permissions/.dm/super_manager/meta.permission.json +52 -0
  112. dmart/sample/spaces/management/permissions/.dm/view_activity_log/meta.permission.json +26 -0
  113. dmart/sample/spaces/management/permissions/.dm/view_collections/meta.permission.json +29 -0
  114. dmart/sample/spaces/management/permissions/.dm/view_logs/meta.permission.json +30 -0
  115. dmart/sample/spaces/management/permissions/.dm/view_roles/meta.permission.json +29 -0
  116. dmart/sample/spaces/management/permissions/.dm/view_users/meta.permission.json +25 -0
  117. dmart/sample/spaces/management/permissions/.dm/view_world/meta.permission.json +31 -0
  118. dmart/sample/spaces/management/permissions/.dm/world/meta.permission.json +35 -0
  119. dmart/sample/spaces/management/permissions.json +1 -0
  120. dmart/sample/spaces/management/requests.json +1 -0
  121. dmart/sample/spaces/management/roles/.dm/dummy/meta.role.json +12 -0
  122. dmart/sample/spaces/management/roles/.dm/logged_in/meta.role.json +18 -0
  123. dmart/sample/spaces/management/roles/.dm/manager/meta.role.json +13 -0
  124. dmart/sample/spaces/management/roles/.dm/meta.folder.json +1 -0
  125. dmart/sample/spaces/management/roles/.dm/moderator/meta.role.json +13 -0
  126. dmart/sample/spaces/management/roles/.dm/super_admin/meta.role.json +14 -0
  127. dmart/sample/spaces/management/roles/.dm/test_role/meta.role.json +13 -0
  128. dmart/sample/spaces/management/roles/.dm/world/meta.role.json +15 -0
  129. dmart/sample/spaces/management/roles.json +1 -0
  130. dmart/sample/spaces/management/schema/.dm/admin_notification_request/attachments.media/meta.ui_schema.json +10 -0
  131. dmart/sample/spaces/management/schema/.dm/admin_notification_request/attachments.media/ui_schema.json +32 -0
  132. dmart/sample/spaces/management/schema/.dm/admin_notification_request/meta.schema.json +1 -0
  133. dmart/sample/spaces/management/schema/.dm/api/meta.schema.json +1 -0
  134. dmart/sample/spaces/management/schema/.dm/folder_rendering/meta.schema.json +1 -0
  135. dmart/sample/spaces/management/schema/.dm/health_check/meta.schema.json +17 -0
  136. dmart/sample/spaces/management/schema/.dm/meta.folder.json +1 -0
  137. dmart/sample/spaces/management/schema/.dm/meta_schema/meta.schema.json +1 -0
  138. dmart/sample/spaces/management/schema/.dm/metafile/meta.schema.json +14 -0
  139. dmart/sample/spaces/management/schema/.dm/notification/meta.schema.json +1 -0
  140. dmart/sample/spaces/management/schema/.dm/system_notification_request/attachments.media/meta.ui_schema.json +10 -0
  141. dmart/sample/spaces/management/schema/.dm/system_notification_request/attachments.media/ui_schema.json +32 -0
  142. dmart/sample/spaces/management/schema/.dm/system_notification_request/meta.schema.json +1 -0
  143. dmart/sample/spaces/management/schema/.dm/view/meta.schema.json +1 -0
  144. dmart/sample/spaces/management/schema/.dm/workflow/meta.schema.json +1 -0
  145. dmart/sample/spaces/management/schema/admin_notification_request.json +89 -0
  146. dmart/sample/spaces/management/schema/api.json +1 -0
  147. dmart/sample/spaces/management/schema/folder_rendering.json +238 -0
  148. dmart/sample/spaces/management/schema/health_check.json +8 -0
  149. dmart/sample/spaces/management/schema/meta_schema.json +74 -0
  150. dmart/sample/spaces/management/schema/metafile.json +153 -0
  151. dmart/sample/spaces/management/schema/notification.json +28 -0
  152. dmart/sample/spaces/management/schema/system_notification_request.json +57 -0
  153. dmart/sample/spaces/management/schema/view.json +23 -0
  154. dmart/sample/spaces/management/schema/workflow.json +87 -0
  155. dmart/sample/spaces/management/schema.json +1 -0
  156. dmart/sample/spaces/management/users/.dm/alibaba/meta.user.json +23 -0
  157. dmart/sample/spaces/management/users/.dm/anonymous/meta.user.json +18 -0
  158. dmart/sample/spaces/management/users/.dm/dmart/meta.user.json +26 -0
  159. dmart/sample/spaces/management/users/.dm/meta.folder.json +14 -0
  160. dmart/sample/spaces/management/workflows/.dm/channel/meta.content.json +1 -0
  161. dmart/sample/spaces/management/workflows/.dm/meta.folder.json +1 -0
  162. dmart/sample/spaces/management/workflows/channel.json +148 -0
  163. dmart/sample/spaces/management/workflows.json +1 -0
  164. dmart/sample/spaces/maqola/.dm/meta.space.json +33 -0
  165. dmart/sample/spaces/personal/.dm/meta.space.json +24 -0
  166. dmart/sample/spaces/personal/people/.dm/meta.folder.json +1 -0
  167. dmart/sample/spaces/personal/people/dmart/.dm/meta.folder.json +1 -0
  168. dmart/sample/spaces/personal/people/dmart/messages/.dm/0b5f7e7f/meta.content.json +1 -0
  169. dmart/sample/spaces/personal/people/dmart/messages/.dm/meta.folder.json +1 -0
  170. dmart/sample/spaces/personal/people/dmart/messages/.dm/mytest/meta.content.json +1 -0
  171. dmart/sample/spaces/personal/people/dmart/messages/0b5f7e7f.json +1 -0
  172. dmart/sample/spaces/personal/people/dmart/messages/mytest.json +1 -0
  173. dmart/sample/spaces/personal/people/dmart/notifications/.dm/meta.folder.json +1 -0
  174. dmart/sample/spaces/personal/people/dmart/private/.dm/inner/meta.content.json +1 -0
  175. dmart/sample/spaces/personal/people/dmart/private/.dm/meta.folder.json +1 -0
  176. dmart/sample/spaces/personal/people/dmart/private/inner.json +1 -0
  177. dmart/sample/spaces/personal/people/dmart/protected/.dm/avatar/meta.content.json +1 -0
  178. dmart/sample/spaces/personal/people/dmart/protected/.dm/meta.folder.json +1 -0
  179. dmart/sample/spaces/personal/people/dmart/protected/avatar.png +0 -0
  180. dmart/sample/spaces/personal/people/dmart/public/.dm/meta.folder.json +1 -0
  181. dmart/sample/test/.gitignore +2 -0
  182. dmart/sample/test/createcontent.json +9 -0
  183. dmart/sample/test/createmedia.json +9 -0
  184. dmart/sample/test/createmedia_entry.json +6 -0
  185. dmart/sample/test/createschema.json +8 -0
  186. dmart/sample/test/createschemawork.json +11 -0
  187. dmart/sample/test/createticket.json +13 -0
  188. dmart/sample/test/data.json +4 -0
  189. dmart/sample/test/deletecontent.json +12 -0
  190. dmart/sample/test/logo.jpeg +0 -0
  191. dmart/sample/test/my.jpg +0 -0
  192. dmart/sample/test/myticket.json +23 -0
  193. dmart/sample/test/resources.csv +12 -0
  194. dmart/sample/test/schema.json +16 -0
  195. dmart/sample/test/temp.json +1 -0
  196. dmart/sample/test/test.dmart +45 -0
  197. dmart/sample/test/ticket_schema.json +23 -0
  198. dmart/sample/test/ticket_workflow.json +85 -0
  199. dmart/sample/test/ticketbody.json +4 -0
  200. dmart/sample/test/ticketcontent.json +14 -0
  201. dmart/sample/test/updatecontent.json +20 -0
  202. dmart/sample/test/workflow_schema.json +68 -0
  203. {dmart-1.4.40.post6.dist-info → dmart-1.4.40.post7.dist-info}/METADATA +1 -1
  204. {dmart-1.4.40.post6.dist-info → dmart-1.4.40.post7.dist-info}/RECORD +207 -5
  205. {dmart-1.4.40.post6.dist-info → dmart-1.4.40.post7.dist-info}/WHEEL +0 -0
  206. {dmart-1.4.40.post6.dist-info → dmart-1.4.40.post7.dist-info}/entry_points.txt +0 -0
  207. {dmart-1.4.40.post6.dist-info → dmart-1.4.40.post7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,27 @@
1
+ {
2
+ "uuid": "4a001e1f-a844-4bb9-a340-7f8eb1a77480",
3
+ "shortname": "archive",
4
+ "is_active": true,
5
+ "displayname": {
6
+ "en": "archive"
7
+ },
8
+ "tags": [],
9
+ "created_at": "2024-09-02T12:55:00.899747",
10
+ "updated_at": "2024-09-02T12:55:00.906988",
11
+ "owner_shortname": "dmart",
12
+ "root_registration_signature": "",
13
+ "primary_website": "",
14
+ "indexing_enabled": false,
15
+ "capture_misses": false,
16
+ "check_health": false,
17
+ "languages": [
18
+ "english"
19
+ ],
20
+ "icon": "",
21
+ "mirrors": [],
22
+ "hide_folders": [],
23
+ "hide_space": true,
24
+ "active_plugins": [
25
+ ],
26
+ "ordinal": 1
27
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "shortname": "dummy",
3
+ "is_active": true,
4
+ "filters": {
5
+ "subpaths": [
6
+ "__ALL__"
7
+ ],
8
+ "resource_types": [
9
+ "__ALL__"
10
+ ],
11
+ "schema_shortnames": [
12
+ "__ALL__"
13
+ ],
14
+ "actions": [
15
+ "query",
16
+ "view",
17
+ "update",
18
+ "create",
19
+ "delete",
20
+ "move",
21
+ "progress_ticket",
22
+ "lock",
23
+ "unlock"
24
+ ]
25
+ },
26
+ "type": "hook",
27
+ "listen_time": "after"
28
+ }
@@ -0,0 +1,6 @@
1
+ from models.core import PluginBase, Event
2
+
3
+
4
+ class Plugin(PluginBase):
5
+ async def hook(self, data: Event):
6
+ print(f"Reached to the dummy custom plugin, data recieved: {data}")
@@ -0,0 +1,12 @@
1
+ {
2
+ "shortname": "missed",
3
+ "is_active": false,
4
+ "filters": {
5
+ "subpaths": ["__ALL__"],
6
+ "resource_types": ["__ALL__"],
7
+ "schema_shortnames": ["__ALL__"],
8
+ "actions": ["view", "query"]
9
+ },
10
+ "type": "hook",
11
+ "listen_time": "before"
12
+ }
@@ -0,0 +1,119 @@
1
+ import json
2
+ import sys
3
+ import aiofiles
4
+ from models.core import ActionType, PluginBase, Event, Space
5
+ from utils.helpers import camel_case
6
+ from utils.redis_services import RedisServices
7
+ from utils.spaces import get_spaces
8
+ from data_adapters.adapter import data_adapter as db
9
+ from models import core
10
+ from models.enums import ContentType, ResourceType
11
+ from utils.settings import settings
12
+ from fastapi.logger import logger
13
+
14
+ class Plugin(PluginBase):
15
+
16
+ def __init__(self) -> None:
17
+ pass
18
+
19
+
20
+ async def hook(self, data: Event):
21
+
22
+ # Type narrowing for PyRight
23
+ if not isinstance(data.shortname, str) and not isinstance(data.attributes, dict):
24
+ logger.error(f"invalid data at missed_entry")
25
+ return
26
+
27
+ spaces = await get_spaces()
28
+ if not Space.parse_raw(spaces[data.space_name]).capture_misses:
29
+ return
30
+
31
+ if data.action_type == ActionType.query and "filter_shortnames" in data.attributes:
32
+ for shortname in data.attributes["filter_shortnames"]:
33
+ data.shortname = shortname
34
+ data.resource_type = data.resource_type or ResourceType.content
35
+ await self.check_miss(data)
36
+
37
+ else:
38
+ await self.check_miss(data)
39
+
40
+
41
+
42
+ async def check_miss(self, data: Event):
43
+ # Type narrowing for PyRight
44
+ if not isinstance(data.shortname, str):
45
+ logger.error(f"data.shortname is None and str is required at missed_entry")
46
+ return
47
+
48
+ class_type = getattr(
49
+ sys.modules["models.core"], camel_case(core.ResourceType(data.resource_type))
50
+ )
51
+ path, filename = db.metapath(data.space_name, data.subpath, data.shortname, class_type)
52
+ if (path/filename).is_file():
53
+ return
54
+
55
+ if data.subpath[0] == "/":
56
+ data.subpath = data.subpath.replace("/", "", 1)
57
+
58
+ miss_shortname = f"{data.subpath}_{data.shortname}"
59
+ if len(miss_shortname) > 32:
60
+ miss_shortname = miss_shortname[:32]
61
+
62
+ miss_file = (
63
+ settings.spaces_folder /
64
+ data.space_name /
65
+ f"misses/{miss_shortname}.json"
66
+ )
67
+ miss_content = None
68
+ if miss_file.is_file():
69
+ async with aiofiles.open(miss_file, "r") as file:
70
+ miss_content = json.loads(await file.read())
71
+
72
+ missed_obj_meta = core.Content(
73
+ shortname=miss_shortname,
74
+ owner_shortname=data.user_shortname,
75
+ is_active=True,
76
+ payload=core.Payload(
77
+ content_type=ContentType.json,
78
+ schema_shortname="miss",
79
+ body=miss_shortname + ".json",
80
+ ),
81
+ )
82
+ async with RedisServices() as redis_services:
83
+ meta_doc_id, meta_json = redis_services.prepate_meta_doc(
84
+ data.space_name, "misses", missed_obj_meta
85
+ )
86
+ if not miss_content:
87
+ await db.save(data.space_name, "misses", missed_obj_meta)
88
+ await redis_services.save_doc(meta_doc_id, meta_json)
89
+ num_of_requests = 1
90
+
91
+ else:
92
+ num_of_requests = miss_content["num_of_requests"] + 1
93
+
94
+ await db.save_payload_from_json(
95
+ data.space_name,
96
+ "misses",
97
+ missed_obj_meta,
98
+ {
99
+ "requested_subpath": data.subpath,
100
+ "requested_shortname": data.shortname,
101
+ "num_of_requests": num_of_requests,
102
+ "actioned": "No",
103
+ },
104
+ )
105
+ payload_dict = {
106
+ **meta_json,
107
+ "requested_subpath": data.subpath,
108
+ "requested_shortname": data.shortname,
109
+ "num_of_requests": num_of_requests,
110
+ "actioned": "No"
111
+
112
+ }
113
+ await redis_services.save_payload_doc(
114
+ space_name=data.space_name,
115
+ subpath="misses",
116
+ meta=missed_obj_meta,
117
+ payload=payload_dict,
118
+ resource_type=ResourceType.content
119
+ )
@@ -0,0 +1,12 @@
1
+ {
2
+ "shortname": "own_changed_notification",
3
+ "is_active": true,
4
+ "filters": {
5
+ "subpaths": ["__ALL__"],
6
+ "resource_types": ["__ALL__"],
7
+ "schema_shortnames": ["__ALL__"],
8
+ "actions": ["update", "delete"]
9
+ },
10
+ "type": "hook",
11
+ "listen_time": "after"
12
+ }
@@ -0,0 +1,65 @@
1
+ import sys
2
+ from models.core import Content, Payload, PluginBase, Event
3
+ from models.enums import ContentType, ResourceType
4
+ from utils.helpers import camel_case
5
+ from data_adapters.adapter import data_adapter as db
6
+ from uuid import uuid4
7
+ from fastapi.logger import logger
8
+
9
+
10
+ class Plugin(PluginBase):
11
+
12
+ async def hook(self, data: Event):
13
+
14
+ # Type narrowing for PyRight
15
+ if not isinstance(data.shortname, str):
16
+ logger.error(f"data.shortname is None and str is required at system_notification_sender")
17
+ return
18
+
19
+ class_type = getattr(
20
+ sys.modules["models.core"], camel_case(ResourceType(data.resource_type))
21
+ )
22
+
23
+ entry = await db.load(
24
+ space_name=data.space_name,
25
+ subpath=data.subpath,
26
+ shortname=data.shortname,
27
+ class_type=class_type,
28
+ user_shortname=data.user_shortname
29
+ )
30
+
31
+ if not entry.owner_shortname or entry.owner_shortname == data.user_shortname:
32
+ return
33
+
34
+
35
+ uuid = uuid4()
36
+ meta_obj = Content(
37
+ uuid=uuid,
38
+ shortname=str(uuid)[:8],
39
+ owner_shortname=data.user_shortname,
40
+ payload=Payload(
41
+ content_type=ContentType.json,
42
+ schema_shortname="notification",
43
+ body=f"{str(uuid)[:8]}.json"
44
+ )
45
+ )
46
+ await db.save(
47
+ "personal",
48
+ f"people/{entry.owner_shortname}/notifications",
49
+ meta_obj,
50
+ )
51
+
52
+ notification_obj = {
53
+ "entry_space": data.space_name,
54
+ "entry_subpath": data.subpath,
55
+ "entry_shortname": data.shortname,
56
+ "action_by": data.user_shortname,
57
+ "action_type": data.action_type,
58
+ "is_read": "no"
59
+ }
60
+ await db.save_payload_from_json(
61
+ "personal",
62
+ f"people/{entry.owner_shortname}/notifications",
63
+ meta_obj,
64
+ notification_obj, # type: ignore
65
+ )
@@ -0,0 +1,14 @@
1
+ {
2
+ "shortname": "reports_stats",
3
+ "is_active": false,
4
+ "filters": {
5
+ "subpaths": [
6
+ "reports"
7
+ ],
8
+ "resource_types": ["__ALL__"],
9
+ "schema_shortnames": ["__ALL__"],
10
+ "actions": ["query"]
11
+ },
12
+ "listen_time": "before",
13
+ "type": "hook"
14
+ }
@@ -0,0 +1,82 @@
1
+ from os import scandir
2
+ from re import sub
3
+ from models.core import PluginBase, Event
4
+ from utils.access_control import access_control
5
+ from data_adapters.adapter import data_adapter as db
6
+ from models.core import Content
7
+ from models.api import Query
8
+ from utils.repository import get_last_updated_entry, serve_query, internal_sys_update_model
9
+ from utils.settings import settings
10
+
11
+
12
+
13
+ class Plugin(PluginBase):
14
+ async def hook(self, data: Event):
15
+ if data.subpath[0] == "/":
16
+ data.subpath = data.subpath[1:]
17
+ reports_path = (
18
+ settings.spaces_folder
19
+ / data.space_name
20
+ / data.subpath
21
+ )
22
+
23
+ reports_iterator = scandir(reports_path)
24
+ for report_entry in reports_iterator:
25
+ if not report_entry.is_file():
26
+ continue
27
+
28
+ shortname, _ = report_entry.name.split(".")
29
+ subpath, class_type = "reports", Content
30
+
31
+ try:
32
+ report_meta = await db.load(
33
+ space_name=data.space_name,
34
+ subpath=subpath,
35
+ shortname=shortname, # type: ignore
36
+ class_type=class_type,
37
+ user_shortname=data.user_shortname,
38
+ )
39
+
40
+ report_payload = await db.load_resource_payload(
41
+ space_name=data.space_name,
42
+ subpath=subpath,
43
+ filename=report_meta.payload.body, # type: ignore
44
+ class_type=class_type,
45
+ )
46
+ except Exception:
47
+ continue
48
+
49
+ report_payload["query"]["search"] = sub(
50
+ r"\$\w*", "", report_payload["query"]["search"]
51
+ )
52
+
53
+ report_query = Query(**report_payload["query"])
54
+ report_query.limit = 0
55
+ redis_query_policies = await access_control.get_user_query_policies(
56
+ data.user_shortname, report_query.space_name, report_query.subpath
57
+ )
58
+ total, records = await serve_query(
59
+ report_query, data.user_shortname, redis_query_policies
60
+ )
61
+ report_last_updated_entry = await get_last_updated_entry(
62
+ data.space_name,
63
+ report_payload["query"]["filter_schema_names"],
64
+ False,
65
+ data.user_shortname,
66
+ )
67
+
68
+ report_updates: dict = {
69
+ "number_of_records": total
70
+ }
71
+ if report_last_updated_entry:
72
+ report_updates["last_updated"] = str(
73
+ report_last_updated_entry.attributes["updated_at"]
74
+ )
75
+
76
+ await internal_sys_update_model(
77
+ data.space_name,
78
+ "reports",
79
+ report_meta,
80
+ report_updates
81
+ )
82
+
@@ -0,0 +1,22 @@
1
+ {
2
+ "shortname": "system_notification_sender",
3
+ "is_active": false,
4
+ "filters": {
5
+ "subpaths": ["__ALL__"],
6
+ "resource_types": ["__ALL__"],
7
+ "schema_shortnames": ["__ALL__"],
8
+ "actions": [
9
+ "progress_ticket",
10
+ "view",
11
+ "create",
12
+ "update",
13
+ "delete",
14
+ "attach",
15
+ "move"
16
+ ]
17
+ },
18
+ "type": "hook",
19
+ "listen_time": "after",
20
+ "created_at": "2022-08-11T11:27:28.866048",
21
+ "updated_at": "2022-08-11T11:27:28.866052"
22
+ }
@@ -0,0 +1,268 @@
1
+ import json
2
+ import requests
3
+ from uuid import uuid4
4
+ from api.user.service import send_sms
5
+ from api.user.router import MANAGEMENT_SPACE, USERS_SUBPATH
6
+ from models.core import Notification, Locator, Translation, User
7
+ from models.enums import NotificationType
8
+ from utils.db import save
9
+ from re import sub as res_sub
10
+ from firebase_admin import credentials, messaging, initialize_app
11
+ from utils.helpers import divide_chunks, flatten_dict, lang_code
12
+ from utils.redis_services import RedisServices
13
+ from utils.settings import settings
14
+ from utils.regex import FILENAME
15
+ import os
16
+ from pathlib import Path
17
+ import utils.db as db
18
+ from fastapi.logger import logger
19
+
20
+
21
+ async def send_notification(
22
+ notification_dict: dict,
23
+ receivers: set,
24
+ entry: dict = {},
25
+ ):
26
+ notification_required_keys = [
27
+ "displayname",
28
+ "description",
29
+ "priority",
30
+ "types",
31
+ ]
32
+ if not all(key in notification_dict for key in notification_required_keys):
33
+ raise Exception(
34
+ "One or more of the Main keys 'displayname', 'description', or 'priority' are missing"
35
+ )
36
+ if isinstance(notification_dict["displayname"], Translation):
37
+ notification_dict["displayname"] = notification_dict["displayname"].dict()
38
+ if isinstance(notification_dict["description"], Translation):
39
+ notification_dict["description"] = notification_dict["description"].dict()
40
+
41
+ for locale in ["ar", "en", "ku"]:
42
+ notification_dict["displayname"][locale] = translate_message_vars(
43
+ notification_dict["displayname"][locale], entry, locale
44
+ )
45
+ notification_dict["description"][locale] = translate_message_vars(
46
+ notification_dict["description"][locale], entry, locale
47
+ )
48
+
49
+ for receiver in receivers:
50
+ if "web" in notification_dict["types"]:
51
+ try:
52
+ if not notification_dict.get("push_only", False):
53
+ await store_notification(notification_dict, receiver, entry)
54
+
55
+ # Send push notification
56
+ requests.post(
57
+ url=f"{settings.websocket_url}/send-message/{receiver}",
58
+ data=json.dumps(
59
+ {
60
+ "title": notification_dict["displayname"],
61
+ "description": notification_dict["description"],
62
+ }
63
+ ),
64
+ )
65
+ except Exception as e:
66
+ logger.error(
67
+ "Notification",
68
+ extra={
69
+ "props": {"title": "FAIL at store_notification", "message": e}
70
+ },
71
+ )
72
+ user = None
73
+ if "mobile" in notification_dict["types"]:
74
+ try:
75
+ user = await db.load(
76
+ space_name=MANAGEMENT_SPACE,
77
+ subpath=USERS_SUBPATH,
78
+ shortname=receiver,
79
+ class_type=User,
80
+ user_shortname="dmart",
81
+ )
82
+ await send_notification_firebase(user, notification_dict, entry)
83
+ except Exception as e:
84
+ logger.error(
85
+ "Notification",
86
+ extra={
87
+ "props": {
88
+ "title": "FAIL at send_notification_firebase",
89
+ "message": e,
90
+ }
91
+ },
92
+ )
93
+ if "sms" in notification_dict["types"]:
94
+ try:
95
+ user = (
96
+ user
97
+ if user
98
+ else await db.load(
99
+ space_name=MANAGEMENT_SPACE,
100
+ subpath=USERS_SUBPATH,
101
+ shortname=receiver,
102
+ class_type=User,
103
+ user_shortname="dmart",
104
+ )
105
+ )
106
+ user_lang_code = lang_code(user.language)
107
+ await send_sms(
108
+ user.msisdn or "",
109
+ notification_dict["displayname"][user_lang_code],
110
+ )
111
+ except Exception as e:
112
+ logger.error(
113
+ "Notification",
114
+ extra={"props": {"title": "FAIL at send_sms", "message": e}},
115
+ )
116
+
117
+
118
+ def translate_message_vars(message: str, entry: dict, locale: str):
119
+ entry_dict = flatten_dict(entry)
120
+ for field, value in entry_dict.items():
121
+ if type(value) == dict and locale in value:
122
+ value = value[locale]
123
+ if field in ["created_at", "updated_at"]:
124
+ message = message.replace(
125
+ f"{{{field}}}", str(value.strftime("%Y-%m-%d %H:%M:%S"))
126
+ )
127
+ else:
128
+ message = message.replace(f"{{{field}}}", str(value))
129
+
130
+ return res_sub(r"\{\w*.*\}", "", message)
131
+
132
+
133
+ async def store_notification(
134
+ notification_dict: dict, receiver_shortname: str, entry: dict = {}
135
+ ):
136
+ if notification_dict["payload"]["schema_shortname"] == "admin_notification_request":
137
+ notification_type = NotificationType.admin
138
+ else:
139
+ notification_type = NotificationType.system
140
+
141
+ entry_locator = None
142
+ if entry:
143
+ entry_locator = Locator(
144
+ space_name=entry["space_name"],
145
+ type=entry["resource_type"],
146
+ schema_shortname=entry["payload"]["schema_shortname"],
147
+ subpath=entry["subpath"],
148
+ shortname=entry["shortname"],
149
+ )
150
+
151
+ uuid = uuid4()
152
+ notification_obj = Notification(
153
+ uuid=uuid,
154
+ shortname=str(uuid)[:8],
155
+ is_active=True,
156
+ displayname=notification_dict["displayname"],
157
+ description=notification_dict["description"],
158
+ owner_shortname=notification_dict["owner_shortname"],
159
+ type=notification_type,
160
+ is_read=False,
161
+ priority=notification_dict["priority"],
162
+ entry=entry_locator,
163
+ )
164
+
165
+ await save(
166
+ space_name="personal",
167
+ subpath=f"people/{receiver_shortname}/notifications",
168
+ meta=notification_obj,
169
+ )
170
+
171
+ async with await RedisServices() as redis:
172
+ await redis.save_meta_doc(
173
+ "personal",
174
+ f"people/{receiver_shortname}/notifications",
175
+ notification_obj,
176
+ )
177
+
178
+
179
+ firebase_app = None
180
+
181
+
182
+ def init_firbase_app():
183
+ global firebase_app
184
+ firebase_cred = credentials.Certificate(settings.google_application_credentials)
185
+ firebase_app = initialize_app(firebase_cred, name="[DEFAULT]")
186
+
187
+
188
+ async def send_notification_firebase(user: User, data, entry):
189
+ users_fcm_tokens = {}
190
+
191
+ firebase_token, msisdn = user.firebase_token, user.msisdn
192
+
193
+ lang = lang_code(user.language)
194
+ # if lang not in users_fcm_tokens:
195
+ users_fcm_tokens[lang] = {}
196
+ users_fcm_tokens[lang][msisdn] = firebase_token
197
+
198
+ images: dict = {}
199
+ images_folder = Path(
200
+ f"{settings.spaces_folder}/{settings.management_space}/{data['subpath']}/.dm/{data['shortname']}/attachments.media"
201
+ )
202
+
203
+ if images_folder.is_dir():
204
+ images_files = os.scandir(images_folder)
205
+ for image_file in images_files:
206
+ match = FILENAME.search(str(image_file.name))
207
+ if not match or not image_file.is_file():
208
+ continue
209
+
210
+ images[
211
+ image_file.name.split("_")[0]
212
+ ] = f"{settings.listening_host}/public/payload/media/{settings.management_space}/{data['subpath']}/{data['shortname']}/{image_file.name}"
213
+
214
+ for lang, fcm_token_per_lang in users_fcm_tokens.items():
215
+ res = send_multiple_notifications(
216
+ tokens=list(fcm_token_per_lang.values()),
217
+ title=data.get("displayname", {}).get(lang, ""),
218
+ body=data.get("description", {}).get(lang, ""),
219
+ image=images.get(lang, ""),
220
+ data={
221
+ **data.get("deep_link"),
222
+ "id": entry.get("shortname"),
223
+ "space_name": entry.get("space_name"),
224
+ "subpath": entry.get("subpath"),
225
+ },
226
+ )
227
+
228
+
229
+ def send_multiple_notifications(
230
+ tokens: list, title: str, body: str, data: dict, image: str, sound="default"
231
+ ) -> list:
232
+ if not firebase_app:
233
+ init_firbase_app()
234
+ alert = messaging.ApsAlert(title=title, body=body)
235
+ aps = messaging.Aps(alert=alert, sound="default", content_available=True)
236
+ apns = messaging.APNSConfig(
237
+ payload=messaging.APNSPayload(aps),
238
+ fcm_options=messaging.APNSFCMOptions(image=image),
239
+ )
240
+ android_notification_settings = messaging.AndroidNotification(
241
+ priority="high", channel_id="FCM_CHANNEL_ID", visibility="public", image=image
242
+ )
243
+ android_config = messaging.AndroidConfig(
244
+ priority="high", notification=android_notification_settings
245
+ )
246
+ web_push = messaging.WebpushConfig(headers={"image": image})
247
+ # Firebase sends message to max of 500 registration tokens.
248
+ tokens_chunks = divide_chunks(tokens, 500)
249
+ failed_tokens = []
250
+ for tokens in tokens_chunks:
251
+ message = messaging.MulticastMessage(
252
+ notification=messaging.Notification(title=title, body=body),
253
+ tokens=tokens,
254
+ data=data,
255
+ apns=apns,
256
+ android=android_config,
257
+ webpush=web_push,
258
+ )
259
+ response = messaging.send_multicast(message)
260
+
261
+ if response.failure_count > 0:
262
+ responses = response.responses
263
+ for idx, resp in enumerate(responses):
264
+ if not resp.success:
265
+ # The order of responses corresponds to the order of the registration tokens.
266
+ failed_tokens.append(tokens[idx])
267
+
268
+ return failed_tokens