django-nativemojo 0.1.10__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 (194) hide show
  1. django_nativemojo-0.1.10.dist-info/LICENSE +19 -0
  2. django_nativemojo-0.1.10.dist-info/METADATA +96 -0
  3. django_nativemojo-0.1.10.dist-info/NOTICE +8 -0
  4. django_nativemojo-0.1.10.dist-info/RECORD +194 -0
  5. django_nativemojo-0.1.10.dist-info/WHEEL +4 -0
  6. mojo/__init__.py +3 -0
  7. mojo/apps/account/__init__.py +1 -0
  8. mojo/apps/account/admin.py +91 -0
  9. mojo/apps/account/apps.py +16 -0
  10. mojo/apps/account/migrations/0001_initial.py +77 -0
  11. mojo/apps/account/migrations/0002_user_is_email_verified_user_is_phone_verified.py +23 -0
  12. mojo/apps/account/migrations/0003_group_mojo_secrets_user_mojo_secrets.py +23 -0
  13. mojo/apps/account/migrations/__init__.py +0 -0
  14. mojo/apps/account/models/__init__.py +3 -0
  15. mojo/apps/account/models/group.py +98 -0
  16. mojo/apps/account/models/member.py +95 -0
  17. mojo/apps/account/models/pkey.py +18 -0
  18. mojo/apps/account/models/user.py +211 -0
  19. mojo/apps/account/rest/__init__.py +3 -0
  20. mojo/apps/account/rest/group.py +25 -0
  21. mojo/apps/account/rest/user.py +47 -0
  22. mojo/apps/account/utils/__init__.py +0 -0
  23. mojo/apps/account/utils/jwtoken.py +72 -0
  24. mojo/apps/account/utils/passkeys.py +54 -0
  25. mojo/apps/fileman/README.md +549 -0
  26. mojo/apps/fileman/__init__.py +0 -0
  27. mojo/apps/fileman/apps.py +15 -0
  28. mojo/apps/fileman/backends/__init__.py +117 -0
  29. mojo/apps/fileman/backends/base.py +319 -0
  30. mojo/apps/fileman/backends/filesystem.py +397 -0
  31. mojo/apps/fileman/backends/s3.py +398 -0
  32. mojo/apps/fileman/examples/configurations.py +378 -0
  33. mojo/apps/fileman/examples/usage_example.py +665 -0
  34. mojo/apps/fileman/management/__init__.py +1 -0
  35. mojo/apps/fileman/management/commands/__init__.py +1 -0
  36. mojo/apps/fileman/management/commands/cleanup_expired_uploads.py +222 -0
  37. mojo/apps/fileman/models/__init__.py +7 -0
  38. mojo/apps/fileman/models/file.py +292 -0
  39. mojo/apps/fileman/models/manager.py +227 -0
  40. mojo/apps/fileman/models/render.py +0 -0
  41. mojo/apps/fileman/rest/__init__ +0 -0
  42. mojo/apps/fileman/rest/__init__.py +23 -0
  43. mojo/apps/fileman/rest/fileman.py +13 -0
  44. mojo/apps/fileman/rest/upload.py +92 -0
  45. mojo/apps/fileman/utils/__init__.py +19 -0
  46. mojo/apps/fileman/utils/upload.py +616 -0
  47. mojo/apps/incident/__init__.py +1 -0
  48. mojo/apps/incident/handlers/__init__.py +3 -0
  49. mojo/apps/incident/handlers/event_handlers.py +142 -0
  50. mojo/apps/incident/migrations/0001_initial.py +83 -0
  51. mojo/apps/incident/migrations/0002_rename_bundle_ruleset_bundle_minutes_event_hostname_and_more.py +44 -0
  52. mojo/apps/incident/migrations/0003_alter_event_model_id.py +18 -0
  53. mojo/apps/incident/migrations/0004_alter_incident_model_id.py +18 -0
  54. mojo/apps/incident/migrations/__init__.py +0 -0
  55. mojo/apps/incident/models/__init__.py +3 -0
  56. mojo/apps/incident/models/event.py +135 -0
  57. mojo/apps/incident/models/incident.py +33 -0
  58. mojo/apps/incident/models/rule.py +247 -0
  59. mojo/apps/incident/parsers/__init__.py +0 -0
  60. mojo/apps/incident/parsers/ossec/__init__.py +1 -0
  61. mojo/apps/incident/parsers/ossec/core.py +82 -0
  62. mojo/apps/incident/parsers/ossec/parsed.py +23 -0
  63. mojo/apps/incident/parsers/ossec/rules.py +124 -0
  64. mojo/apps/incident/parsers/ossec/utils.py +169 -0
  65. mojo/apps/incident/reporter.py +42 -0
  66. mojo/apps/incident/rest/__init__.py +2 -0
  67. mojo/apps/incident/rest/event.py +23 -0
  68. mojo/apps/incident/rest/ossec.py +22 -0
  69. mojo/apps/logit/__init__.py +0 -0
  70. mojo/apps/logit/admin.py +37 -0
  71. mojo/apps/logit/migrations/0001_initial.py +32 -0
  72. mojo/apps/logit/migrations/0002_log_duid_log_payload_log_username.py +28 -0
  73. mojo/apps/logit/migrations/0003_log_level.py +18 -0
  74. mojo/apps/logit/migrations/__init__.py +0 -0
  75. mojo/apps/logit/models/__init__.py +1 -0
  76. mojo/apps/logit/models/log.py +57 -0
  77. mojo/apps/logit/rest.py +9 -0
  78. mojo/apps/metrics/README.md +79 -0
  79. mojo/apps/metrics/__init__.py +12 -0
  80. mojo/apps/metrics/redis_metrics.py +331 -0
  81. mojo/apps/metrics/rest/__init__.py +1 -0
  82. mojo/apps/metrics/rest/base.py +152 -0
  83. mojo/apps/metrics/rest/db.py +0 -0
  84. mojo/apps/metrics/utils.py +227 -0
  85. mojo/apps/notify/README.md +91 -0
  86. mojo/apps/notify/README_NOTIFICATIONS.md +566 -0
  87. mojo/apps/notify/__init__.py +0 -0
  88. mojo/apps/notify/admin.py +52 -0
  89. mojo/apps/notify/handlers/__init__.py +0 -0
  90. mojo/apps/notify/handlers/example_handlers.py +516 -0
  91. mojo/apps/notify/handlers/ses/__init__.py +25 -0
  92. mojo/apps/notify/handlers/ses/bounce.py +0 -0
  93. mojo/apps/notify/handlers/ses/complaint.py +25 -0
  94. mojo/apps/notify/handlers/ses/message.py +86 -0
  95. mojo/apps/notify/management/__init__.py +0 -0
  96. mojo/apps/notify/management/commands/__init__.py +1 -0
  97. mojo/apps/notify/management/commands/process_notifications.py +370 -0
  98. mojo/apps/notify/mod +0 -0
  99. mojo/apps/notify/models/__init__.py +12 -0
  100. mojo/apps/notify/models/account.py +128 -0
  101. mojo/apps/notify/models/attachment.py +24 -0
  102. mojo/apps/notify/models/bounce.py +68 -0
  103. mojo/apps/notify/models/complaint.py +40 -0
  104. mojo/apps/notify/models/inbox.py +113 -0
  105. mojo/apps/notify/models/inbox_message.py +173 -0
  106. mojo/apps/notify/models/outbox.py +129 -0
  107. mojo/apps/notify/models/outbox_message.py +288 -0
  108. mojo/apps/notify/models/template.py +30 -0
  109. mojo/apps/notify/providers/__init__.py +0 -0
  110. mojo/apps/notify/providers/aws.py +73 -0
  111. mojo/apps/notify/rest/__init__.py +0 -0
  112. mojo/apps/notify/rest/ses.py +0 -0
  113. mojo/apps/notify/utils/__init__.py +2 -0
  114. mojo/apps/notify/utils/notifications.py +404 -0
  115. mojo/apps/notify/utils/parsing.py +202 -0
  116. mojo/apps/notify/utils/render.py +144 -0
  117. mojo/apps/tasks/README.md +118 -0
  118. mojo/apps/tasks/__init__.py +11 -0
  119. mojo/apps/tasks/manager.py +489 -0
  120. mojo/apps/tasks/rest/__init__.py +2 -0
  121. mojo/apps/tasks/rest/hooks.py +0 -0
  122. mojo/apps/tasks/rest/tasks.py +62 -0
  123. mojo/apps/tasks/runner.py +174 -0
  124. mojo/apps/tasks/tq_handlers.py +14 -0
  125. mojo/decorators/__init__.py +3 -0
  126. mojo/decorators/auth.py +25 -0
  127. mojo/decorators/cron.py +31 -0
  128. mojo/decorators/http.py +132 -0
  129. mojo/decorators/validate.py +14 -0
  130. mojo/errors.py +88 -0
  131. mojo/helpers/__init__.py +0 -0
  132. mojo/helpers/aws/__init__.py +0 -0
  133. mojo/helpers/aws/client.py +8 -0
  134. mojo/helpers/aws/s3.py +268 -0
  135. mojo/helpers/aws/setup_email.py +0 -0
  136. mojo/helpers/cron.py +79 -0
  137. mojo/helpers/crypto/__init__.py +4 -0
  138. mojo/helpers/crypto/aes.py +60 -0
  139. mojo/helpers/crypto/hash.py +59 -0
  140. mojo/helpers/crypto/privpub/__init__.py +1 -0
  141. mojo/helpers/crypto/privpub/hybrid.py +97 -0
  142. mojo/helpers/crypto/privpub/rsa.py +104 -0
  143. mojo/helpers/crypto/sign.py +36 -0
  144. mojo/helpers/crypto/too.l.py +25 -0
  145. mojo/helpers/crypto/utils.py +26 -0
  146. mojo/helpers/daemon.py +94 -0
  147. mojo/helpers/dates.py +69 -0
  148. mojo/helpers/dns/__init__.py +0 -0
  149. mojo/helpers/dns/godaddy.py +62 -0
  150. mojo/helpers/filetypes.py +128 -0
  151. mojo/helpers/logit.py +310 -0
  152. mojo/helpers/modules.py +95 -0
  153. mojo/helpers/paths.py +63 -0
  154. mojo/helpers/redis.py +10 -0
  155. mojo/helpers/request.py +89 -0
  156. mojo/helpers/request_parser.py +269 -0
  157. mojo/helpers/response.py +14 -0
  158. mojo/helpers/settings.py +146 -0
  159. mojo/helpers/sysinfo.py +140 -0
  160. mojo/helpers/ua.py +0 -0
  161. mojo/middleware/__init__.py +0 -0
  162. mojo/middleware/auth.py +26 -0
  163. mojo/middleware/logging.py +55 -0
  164. mojo/middleware/mojo.py +21 -0
  165. mojo/migrations/0001_initial.py +32 -0
  166. mojo/migrations/__init__.py +0 -0
  167. mojo/models/__init__.py +2 -0
  168. mojo/models/meta.py +262 -0
  169. mojo/models/rest.py +538 -0
  170. mojo/models/secrets.py +59 -0
  171. mojo/rest/__init__.py +1 -0
  172. mojo/rest/info.py +26 -0
  173. mojo/serializers/__init__.py +0 -0
  174. mojo/serializers/models.py +165 -0
  175. mojo/serializers/openapi.py +188 -0
  176. mojo/urls.py +38 -0
  177. mojo/ws4redis/README.md +174 -0
  178. mojo/ws4redis/__init__.py +2 -0
  179. mojo/ws4redis/client.py +283 -0
  180. mojo/ws4redis/connection.py +327 -0
  181. mojo/ws4redis/exceptions.py +32 -0
  182. mojo/ws4redis/redis.py +183 -0
  183. mojo/ws4redis/servers/__init__.py +0 -0
  184. mojo/ws4redis/servers/base.py +86 -0
  185. mojo/ws4redis/servers/django.py +171 -0
  186. mojo/ws4redis/servers/uwsgi.py +63 -0
  187. mojo/ws4redis/settings.py +45 -0
  188. mojo/ws4redis/utf8validator.py +128 -0
  189. mojo/ws4redis/websocket.py +403 -0
  190. testit/__init__.py +0 -0
  191. testit/client.py +147 -0
  192. testit/faker.py +20 -0
  193. testit/helpers.py +198 -0
  194. testit/runner.py +262 -0
mojo/helpers/logit.py ADDED
@@ -0,0 +1,310 @@
1
+ import os
2
+ import sys
3
+ import logging
4
+ import threading
5
+ from decimal import Decimal
6
+ from collections import OrderedDict
7
+ from io import StringIO
8
+ from typing import Optional
9
+ import traceback
10
+ import re
11
+ # import traceback
12
+ # import time
13
+ # from datetime import datetime
14
+ # from binascii import hexlify
15
+ from . import paths
16
+
17
+ # Resolve paths
18
+ LOG_DIR = paths.LOG_ROOT
19
+
20
+ # Ensure log directory exists
21
+ os.makedirs(LOG_DIR, exist_ok=True)
22
+
23
+ # Constants
24
+ MAX_LOG_SIZE = 10 * 1024 * 1024 # 10MB
25
+ LOG_BACKUP_COUNT = 3
26
+ COLOR_LOGS = True
27
+ LOG_MANAGER = None
28
+
29
+ def get_logger(name, filename=None, debug=False):
30
+ global LOG_MANAGER
31
+ if LOG_MANAGER is None:
32
+ LOG_MANAGER = LogManager()
33
+ return LOG_MANAGER.get_logger(name, filename, debug)
34
+
35
+
36
+ def pretty_print(msg):
37
+ out = PrettyLogger.pretty_format(msg)
38
+ print(out)
39
+
40
+ pp = pretty_print
41
+
42
+
43
+ def pretty_format(msg):
44
+ return PrettyLogger.pretty_format(msg)
45
+
46
+
47
+ def color_print(msg, color, end="\n"):
48
+ ConsoleLogger.print_message(msg, color, end)
49
+
50
+
51
+ # Mask sensitive data in the log
52
+ def mask_sensitive_data(text):
53
+ sensitive_patterns = [
54
+ r'("?(password|pwd|secret|token|access_token|api_key|authorization)"?\s*[:=]\s*"?)[^",\s]+',
55
+ r'("?(ssn|credit_card|card_number|pin|cvv)"?\s*[:=]\s*"?)[^",\s]+',
56
+ ]
57
+ for pattern in sensitive_patterns:
58
+ text = re.sub(pattern, r'\1*****', text, flags=re.IGNORECASE)
59
+ return text
60
+
61
+ # Utility: Thread-safe lock handler
62
+ class ThreadSafeLock:
63
+ def __init__(self):
64
+ self.lock = threading.RLock()
65
+
66
+ def __enter__(self):
67
+ self.lock.acquire()
68
+
69
+ def __exit__(self, exc_type, exc_val, exc_tb):
70
+ self.lock.release()
71
+
72
+
73
+ # Log Manager to Handle Multiple Loggers
74
+ class LogManager:
75
+ _instance = None
76
+
77
+ def __new__(cls):
78
+ if cls._instance is None:
79
+ cls._instance = super(LogManager, cls).__new__(cls)
80
+ cls._instance.loggers = {}
81
+ cls._instance.streams = {}
82
+ cls._instance.master_logger = None
83
+ cls._instance.lock = ThreadSafeLock()
84
+ return cls._instance
85
+
86
+ def get_logger(self, name, filename=None, debug=False):
87
+ """Retrieve or create a logger."""
88
+ with self.lock:
89
+ if name in self.loggers:
90
+ return self.loggers[name]
91
+
92
+ level = logging.DEBUG if debug else logging.INFO
93
+ logger = logging.getLogger(name)
94
+ logger.setLevel(level)
95
+
96
+ # Create file handler
97
+ if filename:
98
+ log_path = os.path.join(LOG_DIR, filename)
99
+ file_handler = logging.FileHandler(log_path)
100
+ file_handler.setFormatter(self._get_formatter())
101
+ logger.addHandler(file_handler)
102
+
103
+ # Capture to master logger if exists
104
+ if self.master_logger:
105
+ logger.addHandler(logging.StreamHandler(sys.stdout))
106
+
107
+ self.loggers[name] = Logger(name, filename, logger)
108
+ return self.loggers[name]
109
+
110
+ def set_master_logger(self, logger: logging.Logger):
111
+ """Assign master logger for global logging."""
112
+ with self.lock:
113
+ self.master_logger = logger
114
+
115
+ def _get_formatter(self) -> logging.Formatter:
116
+ return logging.Formatter("%(asctime)s - %(levelname)s - %(name)s: %(message)s")
117
+
118
+
119
+ # Logger Wrapper
120
+ class Logger:
121
+ def __init__(self, name, filename, logger):
122
+ self.name = name
123
+ self.filename = filename
124
+ self.logger = logger
125
+
126
+ def _build_log(self, *args):
127
+ output = []
128
+ for arg in args:
129
+ if isinstance(arg, dict):
130
+ output.append("")
131
+ output.append(PrettyLogger.pretty_format(arg))
132
+ else:
133
+ output.append(str(arg))
134
+ return "\n".join(output)
135
+
136
+ def info(self, *args):
137
+ self.logger.info(self._build_log(*args))
138
+
139
+ def debug(self, *args):
140
+ self.logger.debug(self._build_log(*args))
141
+
142
+ def warning(self, *args):
143
+ self.logger.warning(self._build_log(*args))
144
+
145
+ def error(self, *args):
146
+ self.logger.error(self._build_log(*args))
147
+
148
+ def critical(self, *args):
149
+ self.logger.critical(self._build_log(*args))
150
+
151
+ def exception(self, *args):
152
+ exc_info = sys.exc_info()
153
+ err = None
154
+ if exc_info:
155
+ err = {
156
+ "type": str(exc_info[0]),
157
+ "message": str(exc_info[1]),
158
+ "stack_trace": traceback.format_exception(*exc_info)
159
+ }
160
+ pretty_trace = PrettyLogger.pretty_format(err)
161
+ args = (pretty_trace, *args)
162
+ self.logger.exception(self._build_log(*args))
163
+ return err
164
+
165
+ # Log Formatting with Colors
166
+ class ColorFormatter(logging.Formatter):
167
+ COLORS = {
168
+ "DEBUG": "\033[34m", # Blue
169
+ "INFO": "\033[32m", # Green
170
+ "WARNING": "\033[33m", # Yellow
171
+ "ERROR": "\033[31m", # Red
172
+ "CRITICAL": "\033[35m", # Pink
173
+ "BLUE": "\033[34m", # Blue
174
+ "GREEN": "\033[32m", # Green
175
+ "YELLOW": "\033[33m", # Yellow
176
+ "RED": "\033[31m", # Red
177
+ "PINK": "\033[35m", # Pink
178
+ }
179
+ RESET = "\033[0m"
180
+
181
+ def format(self, record):
182
+ log_color = self.COLORS.get(record.levelname, self.RESET)
183
+ return f"{log_color}{super().format(record)}{self.RESET}"
184
+
185
+
186
+ # Utility for Pretty Logging
187
+ class PrettyLogger:
188
+ @staticmethod
189
+ def pretty_format(data, max_length=500000) -> str:
190
+ """Formats complex data structures for logging."""
191
+ output = StringIO()
192
+ PrettyLogger._recursive_format(data, output, 0, max_length)
193
+ return output.getvalue()
194
+
195
+ @staticmethod
196
+ def _recursive_format(data, output=sys.stdout, indent=0, max_length=80):
197
+ """Recursive function to pretty-print dictionaries and lists with proper indentation and colors."""
198
+
199
+ base_indent = " " * indent # Current level indentation
200
+ next_indent = " " * (indent + 2) # Indentation for nested structures
201
+
202
+ if isinstance(data, dict):
203
+ data = OrderedDict(sorted(data.items())) # Ensure ordered keys
204
+ output.write("{\n") # Open dict at current indent
205
+ last_index = len(data) - 1
206
+ for i, (key, value) in enumerate(data.items()):
207
+ output.write(next_indent + f"\033[34m\"{key}\"\033[0m: ")
208
+ PrettyLogger._recursive_format(value, output, indent + 2, max_length)
209
+ if i != last_index:
210
+ output.write(",") # Add comma for all but last key-value pair
211
+ output.write("\n")
212
+ if indent == 0:
213
+ base_indent = ""
214
+ output.write(base_indent + "}") # Close dict at the correct indent
215
+ elif isinstance(data, list):
216
+ output.write("[\n") # Open list at correct indent
217
+ last_index = len(data) - 1
218
+ for i, item in enumerate(data):
219
+ output.write(next_indent)
220
+ PrettyLogger._recursive_format(item, output, indent + 2, max_length)
221
+ if i != last_index:
222
+ output.write(",") # Add comma for all but last item
223
+ output.write("\n")
224
+ output.write(base_indent + "]") # Close list at correct indent
225
+ elif isinstance(data, Decimal):
226
+ output.write(f"\033[32m{str(data)}\033[0m") # Green for Decimal
227
+ elif isinstance(data, str):
228
+ output.write(f"\033[31m\"{data}\"\033[0m") # Red for strings
229
+ else:
230
+ output.write(f"\033[33m{str(data)}\033[0m") # Yellow for other types
231
+
232
+ @staticmethod
233
+ def log_json(data, logger=None):
234
+ """Logs data in JSON format."""
235
+ if logger is None:
236
+ logger = Logger("root")
237
+ formatted_data = PrettyLogger.pretty_format(data)
238
+ logger.info(formatted_data)
239
+
240
+
241
+ # Console Logger Utility
242
+ class ConsoleLogger:
243
+ BLACK = "\033[30m"
244
+ RED = "\033[31m"
245
+ GREEN = "\033[32m"
246
+ YELLOW = "\033[33m"
247
+ PINK = "\033[35m"
248
+ BLUE = "\033[34m"
249
+ WHITE = "\033[37m"
250
+
251
+ HBLACK = "\033[90m"
252
+ HRED = "\033[91m"
253
+ HGREEN = "\033[92m"
254
+ HYELLOW = "\033[93m"
255
+ HBLUE = "\033[94m"
256
+ HPINK = "\033[95m"
257
+ HWHITE = "\033[97m"
258
+
259
+ HEADER = "\033[95m"
260
+ FAIL = "\033[91m"
261
+ OFF = "\033[0m"
262
+ BOLD = "\033[1m"
263
+ UNDERLINE = "\033[4m"
264
+
265
+ @staticmethod
266
+ def print_message(msg, color_code="\033[32m", end="\n"):
267
+ """Prints a color-coded message to the console."""
268
+ sys.stdout.write(f"{color_code}{msg}\033[0m{end}")
269
+ sys.stdout.flush()
270
+
271
+
272
+ # Rotating File Handler
273
+ class RotatingLogger:
274
+ def __init__(self, log_file="app.log", max_bytes=MAX_LOG_SIZE, backup_count=LOG_BACKUP_COUNT):
275
+ self.logger = logging.getLogger("RotatingLogger")
276
+ self.logger.setLevel(logging.INFO)
277
+
278
+ handler = logging.handlers.RotatingFileHandler(
279
+ os.path.join(LOG_DIR, log_file),
280
+ maxBytes=max_bytes,
281
+ backupCount=backup_count,
282
+ )
283
+ handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
284
+ self.logger.addHandler(handler)
285
+
286
+ def log(self, message, level=logging.INFO):
287
+ self.logger.log(level, message)
288
+
289
+
290
+ # Usage Example
291
+ if __name__ == "__main__":
292
+ print(BASE_DIR)
293
+ log = Logger("AppLogger", "app.log", debug=True)
294
+ log.info("🚀 Application started successfully!")
295
+ log.debug("🔍 Debugging mode enabled")
296
+ log.warning("⚠️ Warning: Low disk space")
297
+ log.error("❌ An error occurred while processing request")
298
+ log.critical("🔥 Critical system failure!")
299
+
300
+ # Pretty print a dictionary
301
+ sample_data = {
302
+ "user": "John Doe",
303
+ "email": "john.doe@example.com",
304
+ "permissions": ["read", "write"],
305
+ "settings": {"theme": "dark", "notifications": True},
306
+ }
307
+ PrettyLogger.log_json(sample_data, log)
308
+
309
+ # Console logger
310
+ ConsoleLogger.print_message("✔ Task completed successfully", "\033[32m")
@@ -0,0 +1,95 @@
1
+ import importlib
2
+ import pkgutil
3
+ import sys
4
+
5
+
6
+ def module_exists(module_name):
7
+ return pkgutil.find_loader(module_name)
8
+
9
+
10
+ def load_module_to_globals(module, memory=None):
11
+ """
12
+ Injects all uppercase variables from the given module into the specified namespace.
13
+
14
+ Args:
15
+ module: The module whose variables should be injected.
16
+ memory (dict, optional): The namespace where variables will be stored.
17
+ Defaults to `globals()`.
18
+ """
19
+ if memory is None:
20
+ memory = globals()
21
+
22
+ if isinstance(module, str):
23
+ module = load_module(module, module)
24
+
25
+ # Extract only variables that are ALL CAPS (Django convention for settings)
26
+ memory.update({key: value for key, value in vars(module).items() if key.isupper()})
27
+
28
+
29
+ def load_module(module_name, package=__name__, ignore_errors=True):
30
+ """
31
+ Import a module by name and inject its variables into the global namespace.
32
+
33
+ Args:
34
+ module_name (str): The name of the module to be imported.
35
+ package (str, optional): The package name to use for relative imports. Defaults to the current package.
36
+
37
+ Raises:
38
+ ImportError: If the module cannot be found in the specified package.
39
+ """
40
+ if ignore_errors:
41
+ try:
42
+ module = importlib.import_module(module_name, package=package)
43
+ return module
44
+ except ModuleNotFoundError:
45
+ pass
46
+ return None
47
+ return importlib.import_module(module_name, package=package)
48
+
49
+ def get_root_module(func, app_root=True):
50
+ """
51
+ Get the root (top-level) module of a function.
52
+
53
+ :param func: The function to inspect.
54
+ :return: The root module name (str) or None if not found.
55
+ """
56
+ if not hasattr(func, "__module__"):
57
+ return None # Not a valid function or method
58
+
59
+ # Fully unwrap to get the original function
60
+ while hasattr(func, "__wrapped__"):
61
+ func = func.__wrapped__
62
+
63
+ module_name = func.__module__ # Get the module where the function is defined
64
+
65
+ if module_name not in sys.modules:
66
+ return None # The module is not loaded
67
+
68
+ parts = module_name.split('.')
69
+
70
+ if app_root:
71
+ # Try to find a module that contains 'models' submodule
72
+ for i in range(len(parts)-1, -1, -1):
73
+ potential_root = '.'.join(parts[:i+1])
74
+ try:
75
+ if module_exists(f"{potential_root}.models") or module_exists(f"{potential_root}.rest"):
76
+ return sys.modules.get(potential_root)
77
+ except ImportError:
78
+ continue
79
+
80
+ # If no models module found or app_root is False, return top-level module
81
+ root_module = parts[0]
82
+ return sys.modules.get(root_module, None)
83
+
84
+
85
+ def get_model(app_name, model_name):
86
+ from django.apps import apps
87
+ return apps.get_model(app_name, model_name)
88
+ # # Import the module containing the models
89
+ # models_module = importlib.import_module(f"{app_name}.models")
90
+ # # Get the model class from the module
91
+ # model = getattr(models_module, model_name)
92
+ # return model
93
+
94
+ def get_model_instance(app_name, model_name, pk):
95
+ return get_model(app_name, model_name).objects.filter(id=pk).last()
mojo/helpers/paths.py ADDED
@@ -0,0 +1,63 @@
1
+ import sys
2
+ import socket
3
+ from pathlib import Path
4
+
5
+
6
+ def configure_paths(base_dir, depth=2):
7
+ """
8
+ Dynamically configure paths based on the provided BASE_DIR.
9
+ Injects computed paths into the global namespace.
10
+ """
11
+ global HOSTNAME, PROJECT_ROOT, VAR_ROOT, BIN_ROOT, LOG_ROOT, CONFIG_ROOT
12
+ global MEDIA_ROOT, MEDIA_URL, STATIC_ROOT, SITE_STATIC_ROOT, STATIC_DATA_ROOT
13
+ global STATIC_URL, STATIC_URL_SECURE, APPS_ROOT, APPS_CONFIG_FILE, STATICFILES_DIRS
14
+
15
+ # System Info
16
+ HOSTNAME = socket.gethostname()
17
+
18
+ # Define Paths
19
+ base_path = Path(base_dir).resolve().parents[depth]
20
+ PROJECT_ROOT = base_path.parent
21
+ VAR_ROOT = PROJECT_ROOT / "var"
22
+ BIN_ROOT = PROJECT_ROOT / "bin"
23
+ APPS_ROOT = PROJECT_ROOT / "apps"
24
+ CONFIG_ROOT = PROJECT_ROOT / "config"
25
+ APPS_CONFIG_FILE = APPS_ROOT / "apps.json"
26
+ LOG_ROOT = VAR_ROOT / "logs"
27
+
28
+ # Media Settings
29
+ MEDIA_ROOT = base_path / "media"
30
+ MEDIA_URL = "/media/"
31
+
32
+ # Static Files Settings
33
+ STATIC_ROOT = base_path / "static"
34
+ SITE_STATIC_ROOT = base_path / "site_static"
35
+ STATIC_DATA_ROOT = SITE_STATIC_ROOT / "json"
36
+ STATIC_URL = "/static/"
37
+ STATIC_URL_SECURE = "/static/"
38
+ STATICFILES_DIRS = [SITE_STATIC_ROOT]
39
+
40
+ MEDIA_URL = '/media/'
41
+ MEDIA_ROOT = base_path / "media"
42
+
43
+
44
+ # Load additional site-packages paths from .site_packages file
45
+ site_packages_file = PROJECT_ROOT / ".site_packages"
46
+
47
+ if site_packages_file.exists():
48
+ with site_packages_file.open("r") as f:
49
+ site_packages_paths = [path.strip() for path in f.readlines() if path.strip()]
50
+
51
+ for path in site_packages_paths:
52
+ site_path = Path(path)
53
+ if site_path.exists() and str(site_path) not in sys.path:
54
+ sys.path.insert(0, str(site_path))
55
+
56
+ # To use this, call configure_paths(BASE_DIR) from your Django settings
57
+
58
+
59
+ def configure_apps():
60
+ from objict import objict
61
+ global INSTALLED_APPS, APPS_CONFIG
62
+ APPS_CONFIG = objict.fromFile(APPS_CONFIG_FILE)
63
+ INSTALLED_APPS = APPS_CONFIG.installed
mojo/helpers/redis.py ADDED
@@ -0,0 +1,10 @@
1
+ from redis import ConnectionPool, StrictRedis
2
+ from mojo.helpers.settings import settings
3
+ REDIS_POOL = None
4
+
5
+
6
+ def get_connection():
7
+ global REDIS_POOL
8
+ if REDIS_POOL is None:
9
+ REDIS_POOL = ConnectionPool(**settings.REDIS_DB)
10
+ return StrictRedis(connection_pool=REDIS_POOL)
@@ -0,0 +1,89 @@
1
+ from objict import objict, nobjict
2
+ from .request_parser import RequestDataParser
3
+
4
+ REQUEST_PARSER = RequestDataParser()
5
+
6
+ def parse_request_data(request):
7
+ """
8
+ Consolidates all GET, POST, JSON body, and FILE data into one objict dict.
9
+ Handles dotted keys and repeated fields.
10
+ """
11
+ return REQUEST_PARSER.parse(request)
12
+
13
+
14
+ # Additional helper function for debugging
15
+ def debug_request_data(request):
16
+ """
17
+ Debug version that shows step-by-step processing
18
+ """
19
+ print("=== DEBUG REQUEST PARSING ===")
20
+ print(f"Method: {request.method}")
21
+ print(f"Content-Type: {getattr(request, 'content_type', 'Not set')}")
22
+ print(f"GET: {dict(request.GET)}")
23
+ print(f"POST: {dict(request.POST)}")
24
+ print(f"FILES: {list(request.FILES.keys())}")
25
+
26
+ result = parse_request_data(request)
27
+ print(f"Final result: {result}")
28
+ return result
29
+
30
+
31
+ def get_referer(request):
32
+ return request.META.get('HTTP_REFERER')
33
+
34
+
35
+ def get_remote_ip(request):
36
+ x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
37
+ if x_forwarded_for:
38
+ ip = x_forwarded_for.split(',')[0]
39
+ else:
40
+ ip = request.META.get('REMOTE_ADDR')
41
+ return ip
42
+
43
+
44
+ def get_device_id(request):
45
+ # Look for 'buid' or 'duid' in GET parameters
46
+ for key in ['__buid__', 'duid', "buid"]:
47
+ if key in request.GET:
48
+ return request.GET[key]
49
+
50
+ # Look for 'buid' or 'duid' in POST parameters
51
+ for key in ['buid', 'duid']:
52
+ if key in request.POST:
53
+ return request.POST[key]
54
+
55
+ return None
56
+
57
+ def get_user_agent(request):
58
+ return request.META.get("HTTP_USER_AGENT", "")
59
+
60
+
61
+ def parse_user_agent(text):
62
+ """
63
+ returns:
64
+ {
65
+ 'user_agent': {
66
+ 'family': 'Mobile Safari',
67
+ 'major': '13',
68
+ 'minor': '5',
69
+ 'patch': None
70
+ },
71
+ 'os': {
72
+ 'family': 'iOS',
73
+ 'major': '13',
74
+ 'minor': '5',
75
+ 'patch': None,
76
+ 'patch_minor': None
77
+ },
78
+ 'device': {
79
+ 'family': 'iPhone',
80
+ 'brand': None,
81
+ 'model': None
82
+ },
83
+ 'string': '...original UA string...'
84
+ }
85
+ """
86
+ if not isinstance(text, str):
87
+ text = get_user_agent(text)
88
+ from ua_parser import user_agent_parser
89
+ return objict.from_dict(user_agent_parser.Parse(text))