From 9c121cb10642b9736d7879f2050f8f9902b0de62 Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 24 Jun 2025 16:35:48 +0200 Subject: [PATCH] fix sharing issue --- sync/utils.py | 46 ++++++++++++++-- sync/views.py | 143 +++++++++++++++++++------------------------------- 2 files changed, 96 insertions(+), 93 deletions(-) diff --git a/sync/utils.py b/sync/utils.py index 07168bf..76afd26 100644 --- a/sync/utils.py +++ b/sync/utils.py @@ -1,7 +1,7 @@ import importlib -from django.apps import apps -from .registry import model_registry from collections import defaultdict +from .registry import model_registry +from .models import BaseModel, SideStoreModel import random import string @@ -50,7 +50,7 @@ class HierarchyOrganizer: def __init__(self): self.levels = [] # List of dictionaries, each representing a level self.item_levels = {} # Keep track of items and their levels: (model_name, id) -> level - self.sharing_related_instances = {} # Keep track of items and their levels: (model_name, id) -> level + self.children = set() def add_item(self, model_name, item_data, level): """ @@ -91,3 +91,43 @@ class HierarchyOrganizer: if cleaned_dict: cleaned_levels.append(cleaned_dict) return cleaned_levels + + def add_relations(self, instance): + self.add_related_parents(instance) + self.add_related_children(instance) + + def add_related_children(self, instance): + instance.get_shared_children(self.children) + + def grouped_children(self): + grouped = defaultdict(list) + for instance in self.children: + class_name = instance.__class__.__name__ + # serializer = get_serializer(instance, class_name) + grouped[class_name].append(instance.data_identifier_dict()) + return dict(grouped) + + def add_related_parents(self, instance, 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 + + 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}') + self.add_item(parent_model_name, parent_data, current_level + 1) + + # Recursively process parent's parents + self.add_related_parents(parent, current_level + 1) diff --git a/sync/views.py b/sync/views.py index 100ff72..afb7ff6 100644 --- a/sync/views.py +++ b/sync/views.py @@ -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)