|
|
|
|
@ -30,6 +30,25 @@ logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
|
# class HierarchyApiView(APIView): |
|
|
|
|
|
|
|
|
|
def instances_to_dict(instances, models_dict): |
|
|
|
|
for instance in instances: |
|
|
|
|
child_model_name = instance.__class__.__name__ |
|
|
|
|
serializer = get_serializer(instance, child_model_name) |
|
|
|
|
if child_model_name not in models_dict: |
|
|
|
|
models_dict[child_model_name] = {} |
|
|
|
|
|
|
|
|
|
models_dict[child_model_name][instance.id] = serializer.data |
|
|
|
|
return models_dict |
|
|
|
|
|
|
|
|
|
def instances_to_data_identifier_dict(instances, models_dict): |
|
|
|
|
for instance in instances: |
|
|
|
|
if isinstance(instance, BaseModel): |
|
|
|
|
child_model_name = instance.__class__.__name__ |
|
|
|
|
if child_model_name not in models_dict: |
|
|
|
|
models_dict[child_model_name] = {} |
|
|
|
|
models_dict[child_model_name][instance.id] = instance.data_identifier_dict() |
|
|
|
|
return models_dict |
|
|
|
|
|
|
|
|
|
def add_children_hierarchy(instance, models_dict): |
|
|
|
|
sync_models = getattr(settings, 'SYNC_MODEL_CHILDREN_SHARING', {}) |
|
|
|
|
if instance.__class__.__name__ in sync_models: |
|
|
|
|
@ -79,30 +98,30 @@ def add_children_recursively(instance, models_dict): |
|
|
|
|
models_dict[child_model_name][child.id] = serializer.data |
|
|
|
|
add_children_recursively(child, models_dict) |
|
|
|
|
|
|
|
|
|
def add_parents_with_hierarchy_organizer(instance, hierarchy_organizer, current_level=0): |
|
|
|
|
""" |
|
|
|
|
Recursively add all parents of an instance to the hierarchy organizer. |
|
|
|
|
Parents are added at a higher level than their children. |
|
|
|
|
""" |
|
|
|
|
parent_models = instance.get_parents_by_model() |
|
|
|
|
# def add_parents_with_hierarchy_organizer(instance, hierarchy_organizer, current_level=0): |
|
|
|
|
# """ |
|
|
|
|
# Recursively add all parents of an instance to the hierarchy organizer. |
|
|
|
|
# Parents are added at a higher level than their children. |
|
|
|
|
# """ |
|
|
|
|
# parent_models = instance.get_parents_by_model() |
|
|
|
|
|
|
|
|
|
for parent_model_name, parent in parent_models.items(): |
|
|
|
|
if isinstance(parent, BaseModel): |
|
|
|
|
store_id = None |
|
|
|
|
if isinstance(parent, SideStoreModel): |
|
|
|
|
store_id = parent.store_id |
|
|
|
|
# for parent_model_name, parent in parent_models.items(): |
|
|
|
|
# if isinstance(parent, BaseModel): |
|
|
|
|
# store_id = None |
|
|
|
|
# if isinstance(parent, SideStoreModel): |
|
|
|
|
# store_id = parent.store_id |
|
|
|
|
|
|
|
|
|
parent_data = { |
|
|
|
|
'model_id': parent.id, |
|
|
|
|
'store_id': store_id |
|
|
|
|
} |
|
|
|
|
# parent_data = { |
|
|
|
|
# 'model_id': parent.id, |
|
|
|
|
# 'store_id': store_id |
|
|
|
|
# } |
|
|
|
|
|
|
|
|
|
# Add parent at the next level |
|
|
|
|
print(f'*** add parent: {parent_model_name}: {parent.id}') |
|
|
|
|
hierarchy_organizer.add_item(parent_model_name, parent_data, current_level + 1) |
|
|
|
|
# # Add parent at the next level |
|
|
|
|
# print(f'*** add parent: {parent_model_name}: {parent.id}') |
|
|
|
|
# hierarchy_organizer.add_item(parent_model_name, parent_data, current_level + 1) |
|
|
|
|
|
|
|
|
|
# Recursively process parent's parents |
|
|
|
|
add_parents_with_hierarchy_organizer(parent, hierarchy_organizer, current_level + 1) |
|
|
|
|
# # Recursively process parent's parents |
|
|
|
|
# add_parents_with_hierarchy_organizer(parent, hierarchy_organizer, current_level + 1) |
|
|
|
|
|
|
|
|
|
def add_parents_recursively(instance, dictionary): |
|
|
|
|
""" |
|
|
|
|
@ -363,15 +382,11 @@ class LogProcessingResult: |
|
|
|
|
def process_revocations(self): |
|
|
|
|
"""Process revocations and their hierarchies.""" |
|
|
|
|
revocations = defaultdict(list) |
|
|
|
|
revocations_parents_organizer = HierarchyOrganizer() |
|
|
|
|
|
|
|
|
|
# logger.info(f'$$$ process_revocations: {len(self.revoke_info)}') |
|
|
|
|
# sync_models = getattr(settings, 'SYNC_MODEL_CHILDREN_SHARING', {}) |
|
|
|
|
revocated_relations_organizer = HierarchyOrganizer() |
|
|
|
|
|
|
|
|
|
# First, collect all revocations |
|
|
|
|
for model_name, items in self.revoke_info.items(): |
|
|
|
|
revocations[model_name].extend(items) |
|
|
|
|
|
|
|
|
|
logger.info(f'$$$ process_revocations for {model_name}, items = {len(items)}') |
|
|
|
|
|
|
|
|
|
# Process parent hierarchies for each revoked item |
|
|
|
|
@ -383,23 +398,20 @@ class LogProcessingResult: |
|
|
|
|
try: |
|
|
|
|
instance = model.objects.get(id=item['model_id']) |
|
|
|
|
logger.info(f'$$$ process revoked item parents of {model_name} : {item['model_id']}') |
|
|
|
|
add_parents_with_hierarchy_organizer(instance, revocations_parents_organizer) |
|
|
|
|
|
|
|
|
|
# if instance.__class__.__name__ in sync_models: |
|
|
|
|
# sharing_related_instances = sharing_related_instances(instance, True) |
|
|
|
|
# logger.info(f'$$$ get shared instances: {len(sharing_related_instances)}') |
|
|
|
|
# revocations = merge_dicts_dedup(revocations, sharing_related_instances) |
|
|
|
|
# # revocations_parents_organizer.sharing_related_instances = instance.sharing_related_instances() |
|
|
|
|
revocated_relations_organizer.add_relations(instance) |
|
|
|
|
|
|
|
|
|
except model.DoesNotExist: |
|
|
|
|
pass |
|
|
|
|
|
|
|
|
|
return revocations, revocations_parents_organizer |
|
|
|
|
children = revocated_relations_organizer.grouped_children() |
|
|
|
|
merged_revocations = merge_dicts(revocations, children) |
|
|
|
|
|
|
|
|
|
return merged_revocations, revocated_relations_organizer |
|
|
|
|
|
|
|
|
|
def get_response_data(self): |
|
|
|
|
"""Construct the complete response data structure.""" |
|
|
|
|
shared, grants = self.process_shared() |
|
|
|
|
revocations, revocations_parents_organizer = self.process_revocations() |
|
|
|
|
revocations, revocated_relations_organizer = self.process_revocations() |
|
|
|
|
|
|
|
|
|
# print(f'self.deletions = {dict(self.deletions)}') |
|
|
|
|
# print(f'self.shared_relationship_sets = {self.shared_relationship_sets}') |
|
|
|
|
@ -413,7 +425,7 @@ class LogProcessingResult: |
|
|
|
|
"shared": dict(shared), |
|
|
|
|
"grants": dict(grants), |
|
|
|
|
"revocations": dict(revocations), |
|
|
|
|
"revocation_parents": revocations_parents_organizer.get_organized_data(), |
|
|
|
|
"revocated_relations": revocated_relations_organizer.get_organized_data(), |
|
|
|
|
"shared_relationship_sets": self.shared_relationship_sets, |
|
|
|
|
"shared_relationship_removals": self.shared_relationship_removals, |
|
|
|
|
"date": self.last_log_date |
|
|
|
|
@ -472,60 +484,11 @@ class DataAccessViewSet(viewsets.ModelViewSet): |
|
|
|
|
return self.queryset.filter(Q(related_user=self.request.user) | Q(shared_with__in=[self.request.user])) |
|
|
|
|
return [] |
|
|
|
|
|
|
|
|
|
def merge_dicts_dedup(dict1, dict2): |
|
|
|
|
"""Merge two dictionaries, combining arrays and removing duplicates""" |
|
|
|
|
all_keys = set(dict1.keys()) | set(dict2.keys()) |
|
|
|
|
|
|
|
|
|
merged = {} |
|
|
|
|
for key in all_keys: |
|
|
|
|
arr1 = dict1.get(key, []) |
|
|
|
|
arr2 = dict2.get(key, []) |
|
|
|
|
# Convert to sets, union them, then back to list to remove duplicates |
|
|
|
|
merged[key] = list(set(arr1) | set(arr2)) |
|
|
|
|
|
|
|
|
|
return merged |
|
|
|
|
|
|
|
|
|
def sharing_related_instances(instance, identifiers_only): |
|
|
|
|
|
|
|
|
|
sync_models = getattr(settings, 'SYNC_MODEL_CHILDREN_SHARING', {}) |
|
|
|
|
# if self.__class__.__name__ in sync_models: |
|
|
|
|
relationships = sync_models[instance.__class__.__name__] |
|
|
|
|
# 'Match': {'team_scores', 'team_registration', 'player_registrations'} |
|
|
|
|
|
|
|
|
|
models_dict = defaultdict(dict) |
|
|
|
|
|
|
|
|
|
print(f'relationships = {relationships}') |
|
|
|
|
current = [instance] |
|
|
|
|
for relationship in relationships: |
|
|
|
|
print(f'> relationship = {relationship}') |
|
|
|
|
values = [] |
|
|
|
|
for item in current: |
|
|
|
|
value = getattr(item, relationship) |
|
|
|
|
|
|
|
|
|
if hasattr(value, 'all') and callable(value.all): |
|
|
|
|
# This is a queryset from a reverse relationship |
|
|
|
|
for related_obj in value.all(): |
|
|
|
|
child_model_name = related_obj.__class__.__name__ |
|
|
|
|
if identifiers_only: |
|
|
|
|
models_dict[child_model_name].append(related_obj.data_identifier_dict()) |
|
|
|
|
else: |
|
|
|
|
serializer = get_serializer(related_obj, child_model_name) |
|
|
|
|
models_dict[child_model_name].append(serializer.data) |
|
|
|
|
# print(f'>>> 1 Added child for {relationship}: {child_model_name}') |
|
|
|
|
values.extend(value.all()) |
|
|
|
|
else: |
|
|
|
|
# This is a single object |
|
|
|
|
child_model_name = value.__class__.__name__ |
|
|
|
|
if identifiers_only: |
|
|
|
|
models_dict[child_model_name].append(value.data_identifier_dict()) |
|
|
|
|
else: |
|
|
|
|
serializer = get_serializer(value, child_model_name) |
|
|
|
|
models_dict[child_model_name].append(serializer.data) |
|
|
|
|
def merge_dicts(dict1, dict2): |
|
|
|
|
result = defaultdict(list) |
|
|
|
|
|
|
|
|
|
# serializer = get_serializer(value, child_model_name) |
|
|
|
|
# models_dict[child_model_name][value.id] = serializer.data |
|
|
|
|
# print(f'>>> 2 Added child for {relationship}: {child_model_name}') |
|
|
|
|
for d in [dict1, dict2]: |
|
|
|
|
for key, value in d.items(): |
|
|
|
|
result[key].extend(value) |
|
|
|
|
|
|
|
|
|
values.append(value) |
|
|
|
|
current = values |
|
|
|
|
return models_dict |
|
|
|
|
return dict(result) |
|
|
|
|
|