dmart 0.1.9__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.
- alembic/__init__.py +0 -0
- alembic/env.py +91 -0
- alembic/scripts/__init__.py +0 -0
- alembic/scripts/calculate_checksums.py +77 -0
- alembic/scripts/migration_f7a4949eed19.py +28 -0
- alembic/versions/0f3d2b1a7c21_add_authz_materialized_views.py +87 -0
- alembic/versions/10d2041b94d4_last_checksum_history.py +62 -0
- alembic/versions/1cf4e1ee3cb8_ext_permission_with_filter_fields_values.py +33 -0
- alembic/versions/26bfe19b49d4_rm_failedloginattempts.py +42 -0
- alembic/versions/3c8bca2219cc_add_otp_table.py +38 -0
- alembic/versions/6675fd9dfe42_remove_unique_from_sessions_table.py +36 -0
- alembic/versions/71bc1df82e6a_adding_user_last_login_at.py +43 -0
- alembic/versions/74288ccbd3b5_initial.py +264 -0
- alembic/versions/7520a89a8467_rm_activesession_table.py +39 -0
- alembic/versions/848b623755a4_make_created_nd_updated_at_required.py +138 -0
- alembic/versions/8640dcbebf85_add_notes_to_users.py +32 -0
- alembic/versions/91c94250232a_adding_fk_on_owner_shortname.py +104 -0
- alembic/versions/98ecd6f56f9a_ext_meta_with_owner_group_shortname.py +66 -0
- alembic/versions/9aae9138c4ef_indexing_created_at_updated_at.py +80 -0
- alembic/versions/__init__.py +0 -0
- alembic/versions/b53f916b3f6d_json_to_jsonb.py +492 -0
- alembic/versions/eb5f1ec65156_adding_user_locked_to_device.py +36 -0
- alembic/versions/f7a4949eed19_adding_query_policies_to_meta.py +60 -0
- api/__init__.py +0 -0
- api/info/__init__.py +0 -0
- api/info/router.py +109 -0
- api/managed/__init__.py +0 -0
- api/managed/router.py +1541 -0
- api/managed/utils.py +1850 -0
- api/public/__init__.py +0 -0
- api/public/router.py +758 -0
- api/qr/__init__.py +0 -0
- api/qr/router.py +108 -0
- api/user/__init__.py +0 -0
- api/user/model/__init__.py +0 -0
- api/user/model/errors.py +14 -0
- api/user/model/requests.py +165 -0
- api/user/model/responses.py +11 -0
- api/user/router.py +1401 -0
- api/user/service.py +270 -0
- bundler.py +44 -0
- config/__init__.py +0 -0
- config/channels.json +11 -0
- config/notification.json +17 -0
- data_adapters/__init__.py +0 -0
- data_adapters/adapter.py +16 -0
- data_adapters/base_data_adapter.py +467 -0
- data_adapters/file/__init__.py +0 -0
- data_adapters/file/adapter.py +2043 -0
- data_adapters/file/adapter_helpers.py +1013 -0
- data_adapters/file/archive.py +150 -0
- data_adapters/file/create_index.py +331 -0
- data_adapters/file/create_users_folders.py +52 -0
- data_adapters/file/custom_validations.py +68 -0
- data_adapters/file/drop_index.py +40 -0
- data_adapters/file/health_check.py +560 -0
- data_adapters/file/redis_services.py +1110 -0
- data_adapters/helpers.py +27 -0
- data_adapters/sql/__init__.py +0 -0
- data_adapters/sql/adapter.py +3210 -0
- data_adapters/sql/adapter_helpers.py +491 -0
- data_adapters/sql/create_tables.py +451 -0
- data_adapters/sql/create_users_folders.py +53 -0
- data_adapters/sql/db_to_json_migration.py +482 -0
- data_adapters/sql/health_check_sql.py +232 -0
- data_adapters/sql/json_to_db_migration.py +454 -0
- data_adapters/sql/update_query_policies.py +101 -0
- data_generator.py +81 -0
- dmart-0.1.9.dist-info/METADATA +64 -0
- dmart-0.1.9.dist-info/RECORD +149 -0
- dmart-0.1.9.dist-info/WHEEL +5 -0
- dmart-0.1.9.dist-info/entry_points.txt +2 -0
- dmart-0.1.9.dist-info/top_level.txt +23 -0
- dmart.py +513 -0
- get_settings.py +7 -0
- languages/__init__.py +0 -0
- languages/arabic.json +15 -0
- languages/english.json +16 -0
- languages/kurdish.json +14 -0
- languages/loader.py +13 -0
- main.py +506 -0
- migrate.py +24 -0
- models/__init__.py +0 -0
- models/api.py +203 -0
- models/core.py +597 -0
- models/enums.py +255 -0
- password_gen.py +8 -0
- plugins/__init__.py +0 -0
- plugins/action_log/__init__.py +0 -0
- plugins/action_log/plugin.py +121 -0
- plugins/admin_notification_sender/__init__.py +0 -0
- plugins/admin_notification_sender/plugin.py +124 -0
- plugins/ldap_manager/__init__.py +0 -0
- plugins/ldap_manager/plugin.py +100 -0
- plugins/local_notification/__init__.py +0 -0
- plugins/local_notification/plugin.py +123 -0
- plugins/realtime_updates_notifier/__init__.py +0 -0
- plugins/realtime_updates_notifier/plugin.py +58 -0
- plugins/redis_db_update/__init__.py +0 -0
- plugins/redis_db_update/plugin.py +188 -0
- plugins/resource_folders_creation/__init__.py +0 -0
- plugins/resource_folders_creation/plugin.py +81 -0
- plugins/system_notification_sender/__init__.py +0 -0
- plugins/system_notification_sender/plugin.py +188 -0
- plugins/update_access_controls/__init__.py +0 -0
- plugins/update_access_controls/plugin.py +9 -0
- pytests/__init__.py +0 -0
- pytests/api_user_models_erros_test.py +16 -0
- pytests/api_user_models_requests_test.py +98 -0
- pytests/archive_test.py +72 -0
- pytests/base_test.py +300 -0
- pytests/get_settings_test.py +14 -0
- pytests/json_to_db_migration_test.py +237 -0
- pytests/service_test.py +26 -0
- pytests/test_info.py +55 -0
- pytests/test_status.py +15 -0
- run_notification_campaign.py +98 -0
- scheduled_notification_handler.py +121 -0
- schema_migration.py +208 -0
- schema_modulate.py +192 -0
- set_admin_passwd.py +55 -0
- sync.py +202 -0
- utils/__init__.py +0 -0
- utils/access_control.py +306 -0
- utils/async_request.py +8 -0
- utils/exporter.py +309 -0
- utils/firebase_notifier.py +57 -0
- utils/generate_email.py +38 -0
- utils/helpers.py +352 -0
- utils/hypercorn_config.py +12 -0
- utils/internal_error_code.py +60 -0
- utils/jwt.py +124 -0
- utils/logger.py +167 -0
- utils/middleware.py +99 -0
- utils/notification.py +75 -0
- utils/password_hashing.py +16 -0
- utils/plugin_manager.py +215 -0
- utils/query_policies_helper.py +112 -0
- utils/regex.py +44 -0
- utils/repository.py +529 -0
- utils/router_helper.py +19 -0
- utils/settings.py +165 -0
- utils/sms_notifier.py +21 -0
- utils/social_sso.py +67 -0
- utils/templates/activation.html.j2 +26 -0
- utils/templates/reminder.html.j2 +17 -0
- utils/ticket_sys_utils.py +203 -0
- utils/web_notifier.py +29 -0
- websocket.py +231 -0
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
#!/usr/bin/env -S BACKEND_ENV=config.env python3
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import json
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from sqlmodel import Session, create_engine, select, col
|
|
7
|
+
from data_adapters.sql.create_tables import (
|
|
8
|
+
Entries,
|
|
9
|
+
Users,
|
|
10
|
+
Attachments,
|
|
11
|
+
Roles,
|
|
12
|
+
Permissions,
|
|
13
|
+
Histories,
|
|
14
|
+
Spaces,
|
|
15
|
+
)
|
|
16
|
+
from models.core import Payload
|
|
17
|
+
from utils.settings import settings
|
|
18
|
+
from models import core
|
|
19
|
+
import base64
|
|
20
|
+
|
|
21
|
+
postgresql_url = f"{settings.database_driver.replace('+asyncpg','+psycopg')}://{settings.database_username}:{settings.database_password}@{settings.database_host}:{settings.database_port}/{settings.database_name}"
|
|
22
|
+
engine = create_engine(postgresql_url, echo=False)
|
|
23
|
+
|
|
24
|
+
def subpath_checker(subpath: str):
|
|
25
|
+
if subpath.endswith("/"):
|
|
26
|
+
subpath = subpath[:-1]
|
|
27
|
+
if not subpath.startswith("/"):
|
|
28
|
+
subpath = "/" + subpath
|
|
29
|
+
return subpath
|
|
30
|
+
|
|
31
|
+
def ensure_directory_exists(path: str):
|
|
32
|
+
os.makedirs(path, exist_ok=True)
|
|
33
|
+
|
|
34
|
+
def clean_json(data: dict):
|
|
35
|
+
if not isinstance(data, dict):
|
|
36
|
+
return data
|
|
37
|
+
|
|
38
|
+
for key, value in list(data.items()):
|
|
39
|
+
if key in ["tags", "mirrors", "active_plugins", "roles", "groups"]:
|
|
40
|
+
continue
|
|
41
|
+
if not value:
|
|
42
|
+
del data[key]
|
|
43
|
+
elif isinstance(value, datetime):
|
|
44
|
+
data[key] = value.isoformat()
|
|
45
|
+
elif isinstance(value, dict):
|
|
46
|
+
clean_json(value)
|
|
47
|
+
|
|
48
|
+
return data
|
|
49
|
+
|
|
50
|
+
def write_json_file(path, data):
|
|
51
|
+
with open(path, "w") as f:
|
|
52
|
+
if data.get("query_policies", False):
|
|
53
|
+
del data["query_policies"]
|
|
54
|
+
clean = clean_json(data)
|
|
55
|
+
json.dump(clean, f, indent=2, default=str)
|
|
56
|
+
|
|
57
|
+
def write_file(path, data):
|
|
58
|
+
with open(path, "w") as f:
|
|
59
|
+
f.write(data)
|
|
60
|
+
|
|
61
|
+
def write_binary_file(path, data):
|
|
62
|
+
with open(path, "wb") as f:
|
|
63
|
+
f.write(data)
|
|
64
|
+
|
|
65
|
+
def process_attachments(session, space_folder):
|
|
66
|
+
attachments = session.exec(select(Attachments)).all()
|
|
67
|
+
for attachment in attachments:
|
|
68
|
+
subpath = subpath_checker(attachment.subpath)
|
|
69
|
+
|
|
70
|
+
parts = subpath.split('/')
|
|
71
|
+
parts.insert(-1, '.dm')
|
|
72
|
+
new_path = '/'.join(parts)
|
|
73
|
+
|
|
74
|
+
dir_path = f"{space_folder}/{attachment.space_name}{new_path}"
|
|
75
|
+
ensure_directory_exists(dir_path)
|
|
76
|
+
|
|
77
|
+
media_path = f"{dir_path}/attachments.{attachment.resource_type}"
|
|
78
|
+
ensure_directory_exists(media_path)
|
|
79
|
+
if attachment.payload.get("body", None) is not None:
|
|
80
|
+
if attachment.payload["content_type"] == 'json':
|
|
81
|
+
write_json_file(f"{media_path}/{attachment.shortname}.json", attachment.payload.get("body", {}))
|
|
82
|
+
attachment.payload["body"] = f"{attachment.shortname}.json"
|
|
83
|
+
else:
|
|
84
|
+
if attachment.media is None:
|
|
85
|
+
print(f"Warning: empty media for @{attachment.space_name}:{attachment.subpath}/{attachment.shortname}")
|
|
86
|
+
continue
|
|
87
|
+
write_binary_file(f"{media_path}/{attachment.payload['body']}", attachment.media)
|
|
88
|
+
_attachment = attachment.model_dump()
|
|
89
|
+
|
|
90
|
+
del _attachment["media"]
|
|
91
|
+
del _attachment["resource_type"]
|
|
92
|
+
write_json_file(f"{media_path}/meta.{attachment.shortname}.json", _attachment)
|
|
93
|
+
|
|
94
|
+
def process_entries(session, space_folder):
|
|
95
|
+
entries = session.exec(select(Entries)).all()
|
|
96
|
+
for entry in entries:
|
|
97
|
+
subpath = subpath_checker(entry.subpath)
|
|
98
|
+
dir_path = f"{space_folder}/{entry.space_name}{subpath}".replace("//", "/") # Ensure absolute path
|
|
99
|
+
ensure_directory_exists(dir_path)
|
|
100
|
+
|
|
101
|
+
if entry.resource_type == "folder":
|
|
102
|
+
dir_meta_path = f"{dir_path}/{entry.shortname}/.dm/".replace("//", "/")
|
|
103
|
+
ensure_directory_exists(dir_meta_path)
|
|
104
|
+
_entry = entry.model_dump()
|
|
105
|
+
body = None
|
|
106
|
+
if _entry.get("payload", None) is not None:
|
|
107
|
+
if _entry.get("payload", None).get("body", None) is not None:
|
|
108
|
+
body = _entry.get("payload", None)["body"]
|
|
109
|
+
_entry["payload"]["body"] = f"{entry.shortname}.json"
|
|
110
|
+
del _entry["space_name"]
|
|
111
|
+
del _entry["subpath"]
|
|
112
|
+
del _entry["resource_type"]
|
|
113
|
+
|
|
114
|
+
write_json_file(f"{dir_meta_path}/meta.folder.json", _entry)
|
|
115
|
+
if body is not None:
|
|
116
|
+
write_json_file(f"{dir_path}/{entry.shortname}.json", body)
|
|
117
|
+
continue
|
|
118
|
+
|
|
119
|
+
dir_meta_path = f"{dir_path}/.dm/{entry.shortname}".replace("//", "/")
|
|
120
|
+
ensure_directory_exists(dir_meta_path)
|
|
121
|
+
|
|
122
|
+
_entry = entry.model_dump()
|
|
123
|
+
del _entry["space_name"]
|
|
124
|
+
del _entry["subpath"]
|
|
125
|
+
del _entry["resource_type"]
|
|
126
|
+
|
|
127
|
+
if entry.payload:
|
|
128
|
+
if "content_type" not in entry.payload:
|
|
129
|
+
print(f"Warning : empty content type for @{entry.space_name}:{entry.subpath}/{entry.shortname}")
|
|
130
|
+
elif entry.payload["content_type"] == core.ContentType.json:
|
|
131
|
+
|
|
132
|
+
if _entry["payload"].get("body", None) is not None:
|
|
133
|
+
if isinstance(_entry["payload"].get("body", None), dict):
|
|
134
|
+
write_json_file(f"{dir_path}/{entry.shortname}.json", _entry["payload"].get("body", None))
|
|
135
|
+
|
|
136
|
+
_entry["payload"]["body"] = f"{entry.shortname}.json"
|
|
137
|
+
|
|
138
|
+
elif entry.payload["content_type"] == core.ContentType.html:
|
|
139
|
+
if _entry["payload"].get("body", None) is not None:
|
|
140
|
+
with open(f"{dir_path}/{entry.shortname}.html", "w", encoding="utf-8") as f:
|
|
141
|
+
f.write(_entry["payload"]["body"])
|
|
142
|
+
_entry["payload"]["body"] = f"{entry.shortname}.html"
|
|
143
|
+
|
|
144
|
+
elif entry.payload["content_type"] == core.ContentType.image:
|
|
145
|
+
if _entry["payload"].get("body", None) is not None:
|
|
146
|
+
body_data = _entry["payload"]["body"]
|
|
147
|
+
|
|
148
|
+
if isinstance(body_data, str):
|
|
149
|
+
if "." in body_data and any(body_data.lower().endswith(ext) for ext in ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.bmp']):
|
|
150
|
+
_entry["payload"]["body"] = body_data
|
|
151
|
+
print(f"Image reference: @{entry.space_name}:{entry.subpath}/{entry.shortname} -> {body_data}")
|
|
152
|
+
else:
|
|
153
|
+
try:
|
|
154
|
+
if len(body_data) % 4 == 0:
|
|
155
|
+
decoded_data = base64.b64decode(body_data, validate=True)
|
|
156
|
+
|
|
157
|
+
extension = "png"
|
|
158
|
+
if "content_sub_type" in entry.payload:
|
|
159
|
+
extension = entry.payload["content_sub_type"].lower()
|
|
160
|
+
|
|
161
|
+
filename = f"{entry.shortname}.{extension}"
|
|
162
|
+
with open(f"{dir_path}/{filename}", "wb") as f:
|
|
163
|
+
f.write(decoded_data)
|
|
164
|
+
_entry["payload"]["body"] = filename
|
|
165
|
+
else:
|
|
166
|
+
print(f"Warning: Invalid image data for @{entry.space_name}:{entry.subpath}/{entry.shortname}")
|
|
167
|
+
_entry["payload"]["body"] = body_data
|
|
168
|
+
except Exception as e:
|
|
169
|
+
print(f"Error processing image @{entry.space_name}:{entry.subpath}/{entry.shortname}: {e}")
|
|
170
|
+
_entry["payload"]["body"] = body_data
|
|
171
|
+
else:
|
|
172
|
+
extension = "png"
|
|
173
|
+
if "content_sub_type" in entry.payload:
|
|
174
|
+
extension = entry.payload["content_sub_type"].lower()
|
|
175
|
+
|
|
176
|
+
filename = f"{entry.shortname}.{extension}"
|
|
177
|
+
with open(f"{dir_path}/{filename}", "wb") as f:
|
|
178
|
+
f.write(body_data)
|
|
179
|
+
_entry["payload"]["body"] = filename
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
else:
|
|
183
|
+
print(f"Unprocessed content type({entry.payload['content_type']}): @{entry.space_name}:{entry.subpath}/{entry.shortname}")
|
|
184
|
+
|
|
185
|
+
if entry.resource_type != "ticket":
|
|
186
|
+
del _entry["state"]
|
|
187
|
+
del _entry["is_open"]
|
|
188
|
+
del _entry["reporter"]
|
|
189
|
+
del _entry["workflow_shortname"]
|
|
190
|
+
del _entry["collaborators"]
|
|
191
|
+
del _entry["resolution_reason"]
|
|
192
|
+
|
|
193
|
+
write_json_file(f"{dir_meta_path}/meta.{entry.resource_type}.json", _entry)
|
|
194
|
+
|
|
195
|
+
def process_users(session, space_folder):
|
|
196
|
+
users = session.exec(select(Users)).all()
|
|
197
|
+
dir_path = f"{space_folder}/management/users" # Ensure absolute path
|
|
198
|
+
for user in users:
|
|
199
|
+
dir_meta_path = f"{dir_path}/.dm/{user.shortname}"
|
|
200
|
+
ensure_directory_exists(dir_meta_path)
|
|
201
|
+
|
|
202
|
+
_user = user.model_dump()
|
|
203
|
+
del _user["space_name"]
|
|
204
|
+
del _user["resource_type"]
|
|
205
|
+
if _user.get("payload", None) and _user["payload"].get("body" , None):
|
|
206
|
+
write_json_file(
|
|
207
|
+
f"{dir_path}/{user.shortname}.json",
|
|
208
|
+
_user["payload"]["body"]
|
|
209
|
+
)
|
|
210
|
+
_user["payload"]["body"] = f"{user.shortname}.json"
|
|
211
|
+
|
|
212
|
+
write_json_file(f"{dir_meta_path}/meta.user.json", _user)
|
|
213
|
+
|
|
214
|
+
def process_roles(session, space_folder):
|
|
215
|
+
roles = session.exec(select(Roles)).all()
|
|
216
|
+
dir_path = f"{space_folder}/management/roles/.dm" # Ensure absolute path
|
|
217
|
+
for role in roles:
|
|
218
|
+
ensure_directory_exists(f"{dir_path}/{role.shortname}")
|
|
219
|
+
|
|
220
|
+
_role = role.model_dump()
|
|
221
|
+
del _role["space_name"]
|
|
222
|
+
del _role["subpath"]
|
|
223
|
+
del _role["resource_type"]
|
|
224
|
+
|
|
225
|
+
write_json_file(f"{dir_path}/{role.shortname}/meta.role.json", _role)
|
|
226
|
+
|
|
227
|
+
def process_permissions(session, space_folder):
|
|
228
|
+
permissions = session.exec(select(Permissions)).all()
|
|
229
|
+
dir_path = f"{space_folder}/management/permissions/.dm"
|
|
230
|
+
for permission in permissions:
|
|
231
|
+
ensure_directory_exists(f"{dir_path}/{permission.shortname}")
|
|
232
|
+
|
|
233
|
+
_permission = permission.model_dump()
|
|
234
|
+
del _permission["space_name"]
|
|
235
|
+
del _permission["subpath"]
|
|
236
|
+
del _permission["resource_type"]
|
|
237
|
+
|
|
238
|
+
write_json_file(f"{dir_path}/{permission.shortname}/meta.permission.json", _permission)
|
|
239
|
+
|
|
240
|
+
def process_histories(session, space_folder):
|
|
241
|
+
histories = session.exec(select(Histories)).all()
|
|
242
|
+
for history in histories:
|
|
243
|
+
dir_path = f"{space_folder}/{history.space_name}" # Ensure absolute path
|
|
244
|
+
ensure_directory_exists(dir_path)
|
|
245
|
+
|
|
246
|
+
file_path = f"{dir_path}{history.subpath}/.dm/{history.shortname}"
|
|
247
|
+
ensure_directory_exists(file_path)
|
|
248
|
+
|
|
249
|
+
_history_one: str = history.model_dump_json()
|
|
250
|
+
_history: dict = json.loads(_history_one)
|
|
251
|
+
_history["shortname"] = "history"
|
|
252
|
+
|
|
253
|
+
del _history["space_name"]
|
|
254
|
+
del _history["subpath"]
|
|
255
|
+
if _history.get("resource_type", None):
|
|
256
|
+
del _history["resource_type"]
|
|
257
|
+
with open(f"{file_path}/history.jsonl", "a+") as f:
|
|
258
|
+
f.write(json.dumps(_history) + "\n")
|
|
259
|
+
|
|
260
|
+
def process_spaces(session, space_folder):
|
|
261
|
+
spaces = session.exec(select(Spaces)).all()
|
|
262
|
+
for space in spaces:
|
|
263
|
+
dir_path = f"{space_folder}/{space.space_name}/.dm/"
|
|
264
|
+
ensure_directory_exists(dir_path)
|
|
265
|
+
|
|
266
|
+
_space = space.model_dump()
|
|
267
|
+
del _space["space_name"]
|
|
268
|
+
del _space["resource_type"]
|
|
269
|
+
|
|
270
|
+
write_json_file(f"{dir_path}/meta.space.json", _space)
|
|
271
|
+
|
|
272
|
+
async def export_data_with_query(query, user_shortname):
|
|
273
|
+
from utils.repository import serve_query
|
|
274
|
+
|
|
275
|
+
space_folder = os.path.relpath(str(settings.spaces_folder))
|
|
276
|
+
|
|
277
|
+
total, records = await serve_query(query, user_shortname)
|
|
278
|
+
|
|
279
|
+
with (Session(engine) as session):
|
|
280
|
+
space = session.exec(select(Spaces).where(col(Spaces.space_name) == query.space_name)).first()
|
|
281
|
+
if space:
|
|
282
|
+
dir_path = f"{space_folder}/{space.space_name}/.dm/"
|
|
283
|
+
ensure_directory_exists(dir_path)
|
|
284
|
+
|
|
285
|
+
_space = space.model_dump()
|
|
286
|
+
del _space["space_name"]
|
|
287
|
+
del _space["resource_type"]
|
|
288
|
+
|
|
289
|
+
write_json_file(f"{dir_path}/meta.space.json", _space)
|
|
290
|
+
if query.subpath and query.subpath != "/":
|
|
291
|
+
path_parts = query.subpath.strip("/").split("/")
|
|
292
|
+
current_path = ""
|
|
293
|
+
|
|
294
|
+
for part in path_parts:
|
|
295
|
+
current_path += f"/{part}"
|
|
296
|
+
|
|
297
|
+
folder = session.exec(select(Entries).where(
|
|
298
|
+
(Entries.space_name == query.space_name) &
|
|
299
|
+
(Entries.subpath == str(current_path.rsplit("/", 1)[0] or "/")) &
|
|
300
|
+
(Entries.shortname == part) &
|
|
301
|
+
(Entries.resource_type == "folder")
|
|
302
|
+
)).first()
|
|
303
|
+
|
|
304
|
+
if folder:
|
|
305
|
+
folder_subpath = subpath_checker(folder.subpath)
|
|
306
|
+
folder_dir_path = f"{space_folder}/{folder.space_name}{folder_subpath}".replace("//", "/")
|
|
307
|
+
ensure_directory_exists(folder_dir_path)
|
|
308
|
+
|
|
309
|
+
dir_meta_path = f"{folder_dir_path}/{folder.shortname}/.dm/".replace("//", "/")
|
|
310
|
+
ensure_directory_exists(dir_meta_path)
|
|
311
|
+
|
|
312
|
+
_folder = folder.model_dump()
|
|
313
|
+
_folder = {
|
|
314
|
+
**_folder,
|
|
315
|
+
**_folder.get("attributes", {})
|
|
316
|
+
}
|
|
317
|
+
if "attributes" in _folder:
|
|
318
|
+
del _folder["attributes"]
|
|
319
|
+
body = None
|
|
320
|
+
if _folder and _folder.get("payload", None) is not None:
|
|
321
|
+
if _folder and _folder.get("payload", {}).get("body", None) is not None:
|
|
322
|
+
body = _folder.get("payload", {}).get("body", None)
|
|
323
|
+
_folder["payload"]["body"] = f"{folder.shortname}.json"
|
|
324
|
+
|
|
325
|
+
del _folder["space_name"]
|
|
326
|
+
del _folder["subpath"]
|
|
327
|
+
del _folder["resource_type"]
|
|
328
|
+
|
|
329
|
+
write_json_file(f"{dir_meta_path}/meta.folder.json", _folder)
|
|
330
|
+
if body is not None:
|
|
331
|
+
write_json_file(f"{folder_dir_path}/{folder.shortname}.json", body)
|
|
332
|
+
|
|
333
|
+
for entry in records:
|
|
334
|
+
subpath = subpath_checker(entry.subpath)
|
|
335
|
+
dir_path = f"{space_folder}/{query.space_name}{subpath}".replace("//", "/") # Ensure absolute path
|
|
336
|
+
ensure_directory_exists(dir_path)
|
|
337
|
+
|
|
338
|
+
if entry.resource_type == "folder":
|
|
339
|
+
dir_meta_path = f"{dir_path}/{entry.shortname}/.dm/".replace("//", "/")
|
|
340
|
+
ensure_directory_exists(dir_meta_path)
|
|
341
|
+
_entry = entry.model_dump()
|
|
342
|
+
body = None
|
|
343
|
+
if _entry.get("payload", None) is not None:
|
|
344
|
+
if _entry.get("payload", {}).get("body", None) is not None:
|
|
345
|
+
body = _entry.get("payload",{}).get("body", None)
|
|
346
|
+
_entry["payload"]["body"] = f"{entry.shortname}.json"
|
|
347
|
+
|
|
348
|
+
del _entry["subpath"]
|
|
349
|
+
del _entry["resource_type"]
|
|
350
|
+
|
|
351
|
+
_entry = {
|
|
352
|
+
**_entry,
|
|
353
|
+
**_entry.get("attributes", {})
|
|
354
|
+
}
|
|
355
|
+
if "attributes" in _entry:
|
|
356
|
+
del _entry["attributes"]
|
|
357
|
+
|
|
358
|
+
write_json_file(f"{dir_meta_path}/meta.folder.json", _entry)
|
|
359
|
+
if body is not None:
|
|
360
|
+
write_json_file(f"{dir_path}/{entry.shortname}.json", body)
|
|
361
|
+
continue
|
|
362
|
+
|
|
363
|
+
dir_meta_path = f"{dir_path}/.dm/{entry.shortname}".replace("//", "/")
|
|
364
|
+
ensure_directory_exists(dir_meta_path)
|
|
365
|
+
|
|
366
|
+
_entry = entry.model_dump()
|
|
367
|
+
del _entry["subpath"]
|
|
368
|
+
del _entry["resource_type"]
|
|
369
|
+
|
|
370
|
+
if entry.attributes.get("payload"):
|
|
371
|
+
if entry.attributes.get("payload", {}).get("content_type") == core.ContentType.json:
|
|
372
|
+
if _entry.get("attributes",{}).get("payload",{}).get("body", None) is not None:
|
|
373
|
+
if isinstance( _entry.get("attributes",{}).get("payload").get("body", None), dict):
|
|
374
|
+
write_json_file(f"{dir_path}/{entry.shortname}.json", _entry.get("attributes",{}).get("payload").get("body", None))
|
|
375
|
+
_entry.get("attributes",{}).get("payload")["body"] = f"{entry.shortname}.json"
|
|
376
|
+
|
|
377
|
+
_entry = {
|
|
378
|
+
**_entry,
|
|
379
|
+
**_entry.get("attributes",{})
|
|
380
|
+
}
|
|
381
|
+
if "attributes" in _entry:
|
|
382
|
+
del _entry["attributes"]
|
|
383
|
+
if "attachments" in _entry:
|
|
384
|
+
del _entry["attachments"]
|
|
385
|
+
|
|
386
|
+
write_json_file(f"{dir_meta_path}/meta.{entry.resource_type}.json", _entry)
|
|
387
|
+
|
|
388
|
+
histories = session.exec(select(Histories).where(
|
|
389
|
+
(Histories.space_name == query.space_name) &
|
|
390
|
+
(Histories.subpath == entry.subpath) &
|
|
391
|
+
(Histories.shortname == entry.shortname)
|
|
392
|
+
)).all()
|
|
393
|
+
|
|
394
|
+
for history in histories:
|
|
395
|
+
file_path = f"{dir_path}/.dm/{entry.shortname}"
|
|
396
|
+
ensure_directory_exists(file_path)
|
|
397
|
+
|
|
398
|
+
_history_one: str = history.model_dump_json()
|
|
399
|
+
_history: dict = json.loads(_history_one)
|
|
400
|
+
_history["shortname"] = "history"
|
|
401
|
+
|
|
402
|
+
del _history["space_name"]
|
|
403
|
+
del _history["subpath"]
|
|
404
|
+
if _history.get("resource_type", None):
|
|
405
|
+
del _history["resource_type"]
|
|
406
|
+
with open(f"{file_path}/history.jsonl", "a+") as f:
|
|
407
|
+
f.write(json.dumps(_history) + "\n")
|
|
408
|
+
|
|
409
|
+
attachments = session.exec(select(Attachments).where(
|
|
410
|
+
(Attachments.space_name == query.space_name) &
|
|
411
|
+
(Attachments.subpath == f"/{entry.subpath}/{entry.shortname}")
|
|
412
|
+
)).all()
|
|
413
|
+
|
|
414
|
+
__attachment = None
|
|
415
|
+
for attachment in attachments:
|
|
416
|
+
__attachment = attachment
|
|
417
|
+
subpath = subpath_checker(attachment.subpath)
|
|
418
|
+
|
|
419
|
+
parts = subpath.split('/')
|
|
420
|
+
parts.insert(-1, '.dm')
|
|
421
|
+
new_path = '/'.join(parts)
|
|
422
|
+
|
|
423
|
+
dir_path = f"{space_folder}/{query.space_name}{new_path}"
|
|
424
|
+
ensure_directory_exists(dir_path)
|
|
425
|
+
|
|
426
|
+
media_path = f"{dir_path}/attachments.{attachment.resource_type}"
|
|
427
|
+
ensure_directory_exists(media_path)
|
|
428
|
+
|
|
429
|
+
attachment_body = None
|
|
430
|
+
if attachment.payload is not None:
|
|
431
|
+
if isinstance(attachment.payload, Payload):
|
|
432
|
+
attachment_body = attachment.payload.body
|
|
433
|
+
else:
|
|
434
|
+
attachment_body = attachment.payload["body"]
|
|
435
|
+
|
|
436
|
+
if attachment_body is not None:
|
|
437
|
+
if isinstance(attachment.payload, dict) and attachment.payload.get("content_type") == 'json':
|
|
438
|
+
write_json_file(f"{media_path}/{attachment.shortname}.json", attachment_body)
|
|
439
|
+
attachment.payload["body"] = f"{attachment.shortname}.json"
|
|
440
|
+
elif isinstance(attachment.payload, dict) and attachment.payload.get("content_type") == 'comment':
|
|
441
|
+
write_json_file(f"{media_path}/{attachment.shortname}.json", attachment_body)
|
|
442
|
+
attachment.payload["body"] = f"{attachment.shortname}.json"
|
|
443
|
+
else:
|
|
444
|
+
if attachment.media:
|
|
445
|
+
write_binary_file(f"{media_path}/{attachment_body}", attachment.media)
|
|
446
|
+
|
|
447
|
+
_attachment = attachment.model_dump()
|
|
448
|
+
|
|
449
|
+
del _attachment["media"]
|
|
450
|
+
del _attachment["resource_type"]
|
|
451
|
+
write_json_file(f"{media_path}/meta.{attachment.shortname}.json", _attachment)
|
|
452
|
+
else:
|
|
453
|
+
with open(f"{dir_meta_path}/meta.{entry.resource_type}.json", "r") as f:
|
|
454
|
+
entry_data = json.load(f)
|
|
455
|
+
if __attachment is not None:
|
|
456
|
+
if "payload" in entry_data:
|
|
457
|
+
entry_data["payload"] = __attachment.payload
|
|
458
|
+
|
|
459
|
+
return space_folder
|
|
460
|
+
|
|
461
|
+
def main():
|
|
462
|
+
space_folder = os.path.relpath(str(settings.spaces_folder))
|
|
463
|
+
|
|
464
|
+
with Session(engine) as session:
|
|
465
|
+
print("Processing spaces...")
|
|
466
|
+
process_spaces(session, space_folder)
|
|
467
|
+
print("Processing entries...")
|
|
468
|
+
process_entries(session, space_folder)
|
|
469
|
+
print("Processing users...")
|
|
470
|
+
process_users(session, space_folder)
|
|
471
|
+
print("Processing roles...")
|
|
472
|
+
process_roles(session, space_folder)
|
|
473
|
+
print("Processing permissions...")
|
|
474
|
+
process_permissions(session, space_folder)
|
|
475
|
+
print("Processing attachments...")
|
|
476
|
+
process_attachments(session, space_folder)
|
|
477
|
+
print("Processing histories...")
|
|
478
|
+
process_histories(session, space_folder)
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
if __name__ == "__main__":
|
|
482
|
+
main()
|