|
|
|
|
@ -1,7 +1,6 @@ |
|
|
|
|
# from django.shortcuts import render |
|
|
|
|
import re |
|
|
|
|
import json |
|
|
|
|
from .serializers import DataAccessSerializer |
|
|
|
|
|
|
|
|
|
from rest_framework import viewsets |
|
|
|
|
from rest_framework.views import APIView |
|
|
|
|
@ -9,6 +8,7 @@ from rest_framework.permissions import IsAuthenticated |
|
|
|
|
from rest_framework.response import Response |
|
|
|
|
from rest_framework import status |
|
|
|
|
|
|
|
|
|
from django.conf import settings |
|
|
|
|
from django.utils import timezone |
|
|
|
|
from django.core.exceptions import ObjectDoesNotExist |
|
|
|
|
from django.db.models import Q |
|
|
|
|
@ -16,6 +16,7 @@ from django.db.models import Q |
|
|
|
|
from collections import defaultdict |
|
|
|
|
from urllib.parse import unquote |
|
|
|
|
|
|
|
|
|
from .serializers import DataAccessSerializer |
|
|
|
|
from .utils import get_serializer, build_serializer_class, get_data, get_serialized_data, HierarchyOrganizer |
|
|
|
|
|
|
|
|
|
from .models import ModelLog, BaseModel, SideStoreModel, DataAccess |
|
|
|
|
@ -24,7 +25,42 @@ from .registry import model_registry, device_registry |
|
|
|
|
|
|
|
|
|
# class HierarchyApiView(APIView): |
|
|
|
|
|
|
|
|
|
def add_children_recursively(instance, updates): |
|
|
|
|
def add_children_hierarchy(instance, models_dict): |
|
|
|
|
sync_models = getattr(settings, 'SYNC_MODEL_CHILDREN_SHARING', {}) |
|
|
|
|
if instance.__class__.__name__ in sync_models: |
|
|
|
|
relationships = sync_models[instance.__class__.__name__] |
|
|
|
|
# 'Match': {'team_scores', 'team_registration', 'player_registrations'} |
|
|
|
|
|
|
|
|
|
# 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__ |
|
|
|
|
serializer = get_serializer(related_obj, child_model_name) |
|
|
|
|
models_dict[child_model_name][related_obj.id] = 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__ |
|
|
|
|
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}') |
|
|
|
|
|
|
|
|
|
values.append(value) |
|
|
|
|
current = values |
|
|
|
|
|
|
|
|
|
else: |
|
|
|
|
add_children_recursively(instance, models_dict) |
|
|
|
|
|
|
|
|
|
def add_children_recursively(instance, models_dict): |
|
|
|
|
""" |
|
|
|
|
Recursively add all children of an instance to the updates dictionary. |
|
|
|
|
""" |
|
|
|
|
@ -33,10 +69,10 @@ def add_children_recursively(instance, updates): |
|
|
|
|
for child_model_name, children in child_models.items(): |
|
|
|
|
for child in children: |
|
|
|
|
if isinstance(child, BaseModel): |
|
|
|
|
print(f'add_children_recursively: {child_model_name}') |
|
|
|
|
# print(f'add_children_recursively: {child_model_name}') |
|
|
|
|
serializer = get_serializer(child, child_model_name) |
|
|
|
|
updates[child_model_name][child.id] = serializer.data |
|
|
|
|
add_children_recursively(child, updates) |
|
|
|
|
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): |
|
|
|
|
""" |
|
|
|
|
@ -63,28 +99,17 @@ def add_parents_with_hierarchy_organizer(instance, hierarchy_organizer, current_ |
|
|
|
|
# Recursively process parent's parents |
|
|
|
|
add_parents_with_hierarchy_organizer(parent, hierarchy_organizer, current_level + 1) |
|
|
|
|
|
|
|
|
|
def add_parents_recursively(instance, dictionary, minimal=False): |
|
|
|
|
def add_parents_recursively(instance, dictionary): |
|
|
|
|
""" |
|
|
|
|
Recursively add all parents of an instance to the updates dictionary. |
|
|
|
|
If minimal=True, only add id and store_id. |
|
|
|
|
""" |
|
|
|
|
parent_models = instance.get_parents_by_model() |
|
|
|
|
|
|
|
|
|
for parent_model_name, parent in parent_models.items(): |
|
|
|
|
if isinstance(parent, BaseModel): |
|
|
|
|
if minimal: |
|
|
|
|
store_id = None |
|
|
|
|
if isinstance(parent, SideStoreModel): |
|
|
|
|
store_id = parent.store_id |
|
|
|
|
dictionary[parent_model_name][parent.id] = { |
|
|
|
|
'model_id': parent.id, |
|
|
|
|
'store_id': store_id |
|
|
|
|
} |
|
|
|
|
else: |
|
|
|
|
serializer = get_serializer(parent, parent_model_name) |
|
|
|
|
dictionary[parent_model_name][parent.id] = serializer.data |
|
|
|
|
|
|
|
|
|
add_parents_recursively(parent, dictionary, minimal) |
|
|
|
|
serializer = get_serializer(parent, parent_model_name) |
|
|
|
|
dictionary[parent_model_name][parent.id] = serializer.data |
|
|
|
|
add_parents_recursively(parent, dictionary) |
|
|
|
|
|
|
|
|
|
class SynchronizationApi(APIView): |
|
|
|
|
permission_classes = [IsAuthenticated] |
|
|
|
|
@ -224,6 +249,7 @@ class LogProcessingResult: |
|
|
|
|
# Initialize all the collections |
|
|
|
|
self.updates = defaultdict(dict) |
|
|
|
|
self.deletions = defaultdict(list) |
|
|
|
|
self.shared_instances = defaultdict(dict) # {model_name: {model_id: instance}} |
|
|
|
|
self.grant_instances = defaultdict(dict) # {model_name: {model_id: instance}} |
|
|
|
|
self.revoke_info = defaultdict(list) # {model_name: [{model_id, store_id}]} |
|
|
|
|
self.shared_relationship_sets = defaultdict(dict) |
|
|
|
|
@ -240,7 +266,7 @@ class LogProcessingResult: |
|
|
|
|
self.updates[log.model_name][log.model_id] = data |
|
|
|
|
elif log.operation == 'DELETE': |
|
|
|
|
self.deletions[log.model_name].append(log.data_identifier_dict()) |
|
|
|
|
elif log.operation == 'GRANT_ACCESS': |
|
|
|
|
elif log.operation == 'SHARED_ACCESS': |
|
|
|
|
# Remove any existing revocations for this model_id |
|
|
|
|
self._remove_revocation(log.model_name, log.model_id) |
|
|
|
|
|
|
|
|
|
@ -249,10 +275,10 @@ class LogProcessingResult: |
|
|
|
|
model = model_registry.get_model(log.model_name) |
|
|
|
|
try: |
|
|
|
|
instance = model.objects.get(id=log.model_id) |
|
|
|
|
self.grant_instances[log.model_name][log.model_id] = instance |
|
|
|
|
self.shared_instances[log.model_name][log.model_id] = instance |
|
|
|
|
except model.DoesNotExist: |
|
|
|
|
pass |
|
|
|
|
elif log.operation == 'REVOKE_ACCESS': |
|
|
|
|
elif log.operation == 'REVOKED_ACCESS': |
|
|
|
|
print(f'revoke access {log.model_id} - {log.store_id}') |
|
|
|
|
|
|
|
|
|
# Remove any existing grants for this model_id |
|
|
|
|
@ -300,25 +326,27 @@ class LogProcessingResult: |
|
|
|
|
if not self.grant_instances[model_name]: |
|
|
|
|
del self.grant_instances[model_name] |
|
|
|
|
|
|
|
|
|
def process_grants(self): |
|
|
|
|
def process_shared(self): |
|
|
|
|
"""Process grants and their hierarchies.""" |
|
|
|
|
shared = defaultdict(dict) |
|
|
|
|
grants = defaultdict(dict) |
|
|
|
|
|
|
|
|
|
# Process each grant instance |
|
|
|
|
for model_name, instances in self.grant_instances.items(): |
|
|
|
|
for model_name, instances in self.shared_instances.items(): |
|
|
|
|
for model_id, instance in instances.items(): |
|
|
|
|
serializer = get_serializer(instance, model_name) |
|
|
|
|
grants[model_name][model_id] = serializer.data |
|
|
|
|
shared[model_name][model_id] = serializer.data |
|
|
|
|
|
|
|
|
|
# Add hierarchies only once per instance |
|
|
|
|
add_children_recursively(instance, grants) |
|
|
|
|
add_children_hierarchy(instance, grants) |
|
|
|
|
add_parents_recursively(instance, grants) |
|
|
|
|
|
|
|
|
|
# Convert to lists |
|
|
|
|
for model_name in shared: |
|
|
|
|
shared[model_name] = list(shared[model_name].values()) |
|
|
|
|
for model_name in grants: |
|
|
|
|
grants[model_name] = list(grants[model_name].values()) |
|
|
|
|
|
|
|
|
|
return grants |
|
|
|
|
return shared, grants |
|
|
|
|
|
|
|
|
|
def process_revocations(self): |
|
|
|
|
"""Process revocations and their hierarchies.""" |
|
|
|
|
@ -347,7 +375,7 @@ class LogProcessingResult: |
|
|
|
|
|
|
|
|
|
def get_response_data(self): |
|
|
|
|
"""Construct the complete response data structure.""" |
|
|
|
|
grants = self.process_grants() |
|
|
|
|
shared, grants = self.process_shared() |
|
|
|
|
revocations, revocations_parents_organizer = self.process_revocations() |
|
|
|
|
|
|
|
|
|
# print(f'self.deletions = {dict(self.deletions)}') |
|
|
|
|
@ -357,6 +385,7 @@ class LogProcessingResult: |
|
|
|
|
return { |
|
|
|
|
"updates": dict(self.updates), |
|
|
|
|
"deletions": dict(self.deletions), |
|
|
|
|
"shared": dict(shared), |
|
|
|
|
"grants": dict(grants), |
|
|
|
|
"revocations": dict(revocations), |
|
|
|
|
"revocation_parents": revocations_parents_organizer.get_organized_data(), |
|
|
|
|
|