Phase 1 (Foundation): - Project restructure (presenton-main → backend/ + frontend/) - Database schema (8 new models, Alembic config, seed script) - Auth (Azure AD SSO + dev bypass, JWT sessions, AuthMiddleware) - RBAC (access_service, rbac_middleware, admin routers) - Audit logging (fire-and-forget, AuditMiddleware, admin router) - i18n (react-i18next with 5 namespace files) Phase 2 (Admin Panel & Client Management): - Admin panel shell (sidebar layout, role guard, 12 pages) - Redux admin slice with 18 async thunks - User management (role changes, deactivation) - Client management (CRUD, brand config, team management) - Brand config editor (colors, fonts, logos, voice rules) - Master deck upload & parser (PPTX → HTML → React pipeline) - Audit log viewer with filters and CSV/JSON export Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
89 lines
3.1 KiB
Python
89 lines
3.1 KiB
Python
from typing import List
|
|
|
|
from models.json_path_guide import JsonPathGuide, DictGuide, ListGuide
|
|
|
|
|
|
def get_dict_paths_with_key(data: dict, key: str) -> List[JsonPathGuide]:
|
|
result = []
|
|
|
|
def _find_paths(obj, current_path: List[DictGuide | ListGuide]):
|
|
if isinstance(obj, dict):
|
|
if key in obj:
|
|
result.append(JsonPathGuide(guides=current_path.copy()))
|
|
for k, v in obj.items():
|
|
new_path = current_path + [DictGuide(key=k)]
|
|
_find_paths(v, new_path)
|
|
elif isinstance(obj, list):
|
|
for i, item in enumerate(obj):
|
|
new_path = current_path + [ListGuide(index=i)]
|
|
_find_paths(item, new_path)
|
|
|
|
_find_paths(data, [])
|
|
return result
|
|
|
|
|
|
def get_dict_at_path(data: dict, path: JsonPathGuide) -> dict:
|
|
current = data
|
|
for guide in path.guides:
|
|
if isinstance(guide, DictGuide):
|
|
current = current[guide.key]
|
|
elif isinstance(guide, ListGuide):
|
|
current = current[guide.index]
|
|
return current
|
|
|
|
|
|
def set_dict_at_path(data: dict, path: JsonPathGuide, value: dict):
|
|
current = data
|
|
for guide in path.guides[:-1]:
|
|
if isinstance(guide, DictGuide):
|
|
current = current[guide.key]
|
|
elif isinstance(guide, ListGuide):
|
|
current = current[guide.index]
|
|
|
|
if path.guides:
|
|
final_guide = path.guides[-1]
|
|
if isinstance(final_guide, DictGuide):
|
|
current[final_guide.key] = value
|
|
elif isinstance(final_guide, ListGuide):
|
|
current[final_guide.index] = value
|
|
|
|
|
|
def deep_update(original: dict, updates: dict) -> dict:
|
|
for key, value in updates.items():
|
|
if key in original:
|
|
if isinstance(original[key], dict) and isinstance(value, dict):
|
|
deep_update(original[key], value)
|
|
elif isinstance(original[key], list) and isinstance(value, list):
|
|
if len(value) == 0:
|
|
continue
|
|
elif len(value) == 1 and isinstance(value[0], dict):
|
|
if len(original[key]) > 0 and isinstance(original[key][0], dict):
|
|
deep_update(original[key][0], value[0])
|
|
else:
|
|
original[key][0] = (
|
|
value[0] if len(original[key]) > 0 else value[0]
|
|
)
|
|
else:
|
|
min_length = min(len(original[key]), len(value))
|
|
for i in range(min_length):
|
|
if isinstance(original[key][i], dict) and isinstance(
|
|
value[i], dict
|
|
):
|
|
deep_update(original[key][i], value[i])
|
|
else:
|
|
original[key][i] = value[i]
|
|
elif not isinstance(value, (dict, list)):
|
|
original[key] = value
|
|
else:
|
|
if not isinstance(value, (dict, list)):
|
|
original[key] = value
|
|
return original
|
|
|
|
|
|
def has_more_than_n_keys(obj: dict[str, object], n: int) -> bool:
|
|
i = 0
|
|
for _ in obj.keys():
|
|
i += 1
|
|
if i > n:
|
|
return True
|
|
return False
|